<!doctype html>
<!--
based on alchi/src/whoaremyfriends/whoaremyfriends.html
-->
<html lang="de">
<head>
  <meta charset="utf-8">
  <link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />
  <link rel="schema.dcterms" href="http://purl.org/dc/terms/" />

  <title>Dorfkultur ist Zukunft. Aber wer sind meine Freunde?</title>

  <!-- TODO -->
  <meta name="description" content="">

  <meta name="author" content="Milan Hauth">

  <!-- meta#version -->
  <meta id="version" name="dcterms.modified" content="2022-08-22">

  <!-- document ID for hypothesis annotations -->
  <link rel="canonical" href="https://milahu.github.io/alchi/src/dorfkultur/dorfkultur.html#/cas/git/commit/9e77457a15181bcb1aa1b88ffaab4f0d9a6bd84a">

  <!--
    content starts at <article id="content">

    all assets are inlined into this html file
    i hope you have a good code editor, with code folding.
    folding suggestions: class="foldme"
  -->

  <meta class="foldme" name="keywords" content="
    who are my friends,
    team composition,
    matchmaking,
    offline matchmaking,
    interpersonal compatibility,
    teamwork,
    collaboration,
    mate selection,
    self organization,
    self governance,
    bottom up organization,
    extended families,
    village culture,
    ecovillages,
    small is beautiful,
    small states,
    minarchy,
    human compatibility chart,
    human compatibility algorithm,
    human compatibility system,
    human relations,
    human resource managment,
    management,
    relationship management,
    personality type compatibility chart,
    personality type compatibility algorithm,
    personality type compatibility system,
    personality type,
    compatibility algorithm,
    compatibility chart,
    compatibility system,
    compatibility chart without calender astrology,
    scientific compatibility chart,
    dating algorithm,
    dating formula,
    love algorithm,
    love formula,
    dunbar's number,
    utopia,
    paradise on earth,
    real communism,
    small communism,
    village communism,
    all talents are beautiful,
    naturalism,
    natural order,
    bioconservative,
    bioconservatism,
    natural philosophy,
    natural religion,
    primitive culture,
    simple solutions,
    simple and effective,
    radical solutions,
    dualism,
    feedback,
    feedback loop,
    sociology,
    social psychology,
    work life balance,
    four elements,
    four basetypes,
    two factor model of personality,
    radical,
    concept,
    intuitive,
    intuition,
    prediction,
    hypothesis,
    research proposal,
    research idea,
    world formula,
    theory of everybody,
    theory of everything,
    idealism,
    wild theory,
    alternative medicine,
    irrational,
    irrational world view,
    esoteric,
    esoteric cosmology,
    alternative,
    my kingdom is not of this world,
    heretic,
    dissident,
    criminal,
    opposition,
    fundamental opposition,
    subculture,
    small groups,
    gangs,
    gang culture,
    alternative culture,
    alternative religion,
    postcollapse,
    post-collapse culture,
    postcollapse culture,
    postcrash,
    post crash,
    postmodern culture,
    subjectivity,
    nietzsche,
    friedrich nietzsche,
    ayn rand,
    individualism,
    liberalism,
    liberal,
    classical liberal,
    classic liberal,
    liberation ideology,
    liberation theology,
    abuse of power,
    toxic rational,
    toxic rationality,
    balance,
    justice,
    natural selection,
    utopia,
    renaturation,
    run your family like a business,
    manage family like a business,
    rational family management,
    efficient family management,
    organize your family,
    family organization,
    family business,
    emotional aspects of human resource management,
    personality psychology in human resource management,
    small business,
    managing talent in a family business,
    organizational psychology,
    industrial psychology,
    early education,
    talent scouting,
    primitive culture,
    agile development,
    agile development in family,
    agile family,
    family meeting,
    core values,
    long-term strategies and short-term tactics,
    talent management,
    recruiting,
    talent scouting,
    personnel selection,
    job performance,
    hybrid theory,
    both are right,
  ">

  <!-- files/alchi-pallas-symbol.small.svg -->
  <link rel="icon" type="image/svg" href="data:image/svg+xml;charset=UTF-8,%3Csvg%20version%3D%221.1%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cg%20id%3D%22pallas-symbol%22%3E%0A%3Crect%20x%3D%223%22%20y%3D%223%22%20width%3D%2210%22%20height%3D%2210%22%20fill%3D%22%23fff%22%20stroke%3D%22%23fff%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%226%22%2F%3E%0A%3Cpath%20d%3D%22m8%2B10%204-4%20-4-4%20-4%2B4%20z%20m0%200v4%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%221.4%22%2F%3E%0A%3Cpath%20d%3D%22m5%2012h6%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- load annotations -->
  <!-- hypothesis only works online, so this script is external -->
  <script async src="https://hypothes.is/embed.js"></script>

  <!-- non-standard tags, human readable -->
  <!-- TODO maybe use dc tags -->
  <meta name="author.email" content="milahu@gmail.com">
  <meta name="author.chat.matrix" content="@milahu:matrix.org">
  <meta name="author.social.reddit" content="https://www.reddit.com/user/milahu">
  <meta name="author.social.reddit.censored" content="https://www.reveddit.com/y/milahu/">
  <meta name="author.social.gab" content="https://gab.com/milahu">
  <meta name="author.social.facebook" content="https://www.facebook.com/mila.nautikus"><!-- note: "You must log in to continue" -->
  <meta name="author.email.tor" content="milahu@torbox36ijlcevujx7mjb4oiusvwgvmue7jfn2cvutwa6kl6to3uyqad.onion">

  <meta name="homepage" content="https://github.com/milahu/alchi">
  <meta name="homepage.gitea" content="https://try.gitea.io/milahu/alchi">
  <meta name="homepage.gitlab" content="https://gitlab.com/milahu/alchi">
  <meta name="homepage.tor.darktea" content="http://it7otdanqu7ktntxzm427cba6i53w6wlanlh23v5i3siqmos47pzhvyd.onion/milahu/alchi">

  <!-- legal stuff. short: zero limits and zero warranty -->
  <meta class="foldme" name="dc.rights" content='

    MIT License

    Copyright (c) 2022 Milan Hauth

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

  '>

  <style class="foldme">
    /* FIXME width 1105px, zoom 100%, dpi 144? -> horizontal scrollbar in grid view */
    body.js-enabled hr {
      height: 0;
      border: none;
    }
    body {
      font-family: sans-serif;
      font-size: 10pt;
    }
    h1 {
      font-size: 250%;
    }
    /* debug
    * {
      outline: solid 1px red;
    }
    */
    .bindery-print-sheet {
      height: auto !important; /* workaround https://github.com/evnbr/bindery/issues/109 */
    }
    /* hide bindery menu on scroll down */
    .bindery-controls {
      /* position: sticky; */
      position: static !important;
    }
    .bindery-sup {
      vertical-align: baseline;
      font-size: 100% !important;
    }
    /* moved to javascript. when copying text, square braces should be copied too.
    .bindery-sup:before {
      content: " [";
    }
    .bindery-sup:after {
      content: "]";
    }
    */
    /*
      Hypothesis sidebar appears in PDF export https://github.com/manubot/rootstock/issues/280
      probably blame the bindery style
    */
    @media only print {
      hypothesis-sidebar {
        display: none !important;
      }
    }
    .smalltext {
      font-size: 80%; /* this has no effect */
      /* browsers enforce a minimum font size. default 9px. can be more */
      /* https://stackoverflow.com/questions/2421056/how-can-i-override-the-minimum-font-size-in-firefox/12631243 */
    }
    .smalltext-block {
      /* workaround for browser's minimum font size */
      --bindery-content-width: calc(var(--bindery-page-width) - var(--bindery-margin-inner) - var(--bindery-margin-outer));
      width: calc(1.25 * var(--bindery-content-width));
      transform: scale(0.8);
      transform-origin: top left;
      /*margin-bottom: -5em; TODO calc this with javascript? */
    }
    div.para {
      margin-top: 0.5em;
      line-height: 120%;
    }
    .nowrap {
      display: inline;
      white-space: nowrap;
    }
    .footnote {
      white-space: nowrap;
    }
    /* make urls left-aligned */
    .footnote-number {
      display: inline-block;
      width: 1.8em;
      text-align: right;
    }
    .bindery-running-header {
      --bindery-content-width: calc(var(--bindery-page-width) - var(--bindery-margin-inner) - var(--bindery-margin-outer));
      width: var(--bindery-content-width);
    }
    .running-header {
      display: flex;
      flex-direction: row;
      justify-content: space-between;
    }
    .running-header-title-left {
      float: right;
    }
    .running-header-title.hover a {
      color: orange;
    }
    /* css parent selector not implemented
      https://stackoverflow.com/questions/1014861
    .running-header-title-left a:hover ~ .running-header-title-right a {
      color: orange;
    }
    */
    /*
    .footnote-number:before {
      content: " [";
    }
    .footnote-number:after {
      content: "]";
    }
    */
    table.four-basetypes td {
      border-left: solid 1px black; /* FIXME pixel hinting */
      text-align: center;
      padding-left: 5px;
    }
    table.four-basetypes th {
      border-right: solid 1px black; /* FIXME pixel hinting */
      text-align: left;
      padding-right: 5px;
      font-weight: normal;
    }
    table.four-basetypes {
      border-spacing: 2px 0;
      white-space: nowrap;
      font-size: 90%; /* FIXME this has no effect */
      /* workaround for smaller font size */
      /*
      transform: scale(0.86);
      margin-bottom: -2.5em;
      */
      transform-origin: top left;
    }
    a {
      text-decoration: none;
      color: blue;
    }
    a:hover {
      color: orange;
    }
    @media print {
      a { color: black; }
    }
  </style>

  <!--
    inlined at end of file
    <script src="./node_modules/bindery/dist/bindery.umd.js"></script>
  -->

  <script title="binderyOptions" class="foldme">

    const lastPage = 16; // TODO update manually

    // called at end of file: Bindery.makeBook(binderyOptions(Bindery))
    // FIXME bindery bug: cannot select text in print view = cannot create annotations
    function binderyOptions(Bindery) {
      return {
        content: '#content',
        rules: [
          Bindery.PageBreak({ selector: '.pagebreak', position: 'after' }),
          Bindery.PageBreak({ selector: 'hr', position: 'after' }),
          /*
          Bindery.PageBreak({ selector: 'h2:not(.nopagebreak)', position: 'before' }),
          Bindery.PageBreak({ selector: 'h3:not(.nopagebreak)', position: 'before' }),
          */
          // https://github.com/evnbr/bindery/issues/7
          Bindery.Footnote({
            selector: "a:not(.nofootnote)",
            render: (el, number) => {
              var hrefPretty = el.href.replace(/https?:\/\//g, '');
              //return `<span class="footnote"><span class="footnote-number">${ number }</span> <a href="${ el.href }">${ hrefPretty }</a></span>`;
              return `<span class="footnote"><span class="footnote-number">[${ number }]</span> <a href="${ el.href }">${ hrefPretty }</a></span>`;
            },
          }),
          Bindery.RunningHeader({
            render: (page) => {
              if (page.isEmpty) return "";
              //if (page.number <= 2) return "";
              //if (page.number <= 3) return "";
              if (page.number <= 1) return "";
              //if (page.element.querySelector("h1")) return "";
              //if (page.isLeft) return `<div class="running-header"><span class="page-number">${ page.number }</span> ${ page.heading.h1 }</div>`;
              /*
              // show only page number, no link, no version
              if (page.number <= 3) {
                headerTitle = '<div></div>';
                if (page.isLeft) return `<div class="running-header"><div class="page-number">${ page.number }</div>${headerTitle}</div>`;
                if (page.isRight) return `<div class="running-header">${headerTitle}<span class="page-number">${ page.number }</span></div>`;
              }
              */
              var linkList = [
                ['github.com/milahu/alchi', ''],
                ['gitlab.com/milahu/alchi', 'Mirror'],
              ];
              var linkOffset = 1; // which link is first in linkList
              var linkIdx = (Math.floor(page.number / 2) + linkOffset) % linkList.length;
              var link = linkList[linkIdx][0];
              var desc = linkList[linkIdx][1];
              var href = `https://${link}`;
              var linkText = desc ? `${link} (${desc})` : link;
              /* pretty effect, but version is missing
              var headerTitle = (page.isLeft)
                ? `<div class="running-header-title running-header-title-left running-header-title-pages-${page.number}-${(page.number + 1)}"><a href="${href}" onmouseover="hoverBothLinksStart(this)" onmouseout="hoverBothLinksEnd(this)">${linkLeft}</a></div>`
                : `<div class="running-header-title running-header-title-right running-header-title-pages-${(page.number - 1)}-${page.number}"><a href="${href}" onmouseover="hoverBothLinksStart(this)" onmouseout="hoverBothLinksEnd(this)">${linkRight}</a></div>`
              ;
              */
              // <meta id="version"
              var version = document.querySelector('meta#version').content;
              var versionString = version;
              //versionString = `Entwurf ${versionString}`; // add prefix
              var headerTitle = (!page.isLeft)
                ? `<div class="running-header-title running-header-title-left"><a href="${href}" onmouseover="hoverBothLinksStart(this)" onmouseout="hoverBothLinksEnd(this)">${linkText}</a></div>`
                : `<div class="running-header-title running-header-title-right">${versionString}</div>`
              ;

              //const lastPage = 16; // TODO update manually

              // FIXME get num pages
              if (page.number == lastPage) return '';
              if (page.number > lastPage) {
                console.log(`FIXME increase lastPage`);
                alert(`FIXME increase lastPage`); // TODO test
                return '';
              }

              const pageNumber = page.number - 1; // workaround

              if (page.isLeft) return `<div class="running-header"><div class="page-number">${ pageNumber }</div>${headerTitle}</div>`;
              if (page.isRight) return `<div class="running-header">${headerTitle}<span class="page-number">${ pageNumber }</span></div>`;
            },
          }),
        ],
        printSetup: {
          // https://bindery.info/docs/#printsetup
          // setLayout(newVal) -> automatically set paper size from layout https://github.com/evnbr/bindery/issues/112
          layout: Bindery.Layout.SPREADS, // PAGES SPREADS BOOKLET
          paper: Bindery.Paper.A4_LANDSCAPE, // A5_PORTRAIT A4_LANDSCAPE
          marks: Bindery.Marks.NONE, // NONE CROP BLEED BOTH
          bleed: '-12pt', // workaround: bleed is biased by +12pt -> padding-top: calc(var(--bindery-bleed) + 12pt);
        },
        pageSetup: {
          size: { width: '148mm', height: '209mm' }, // A5. height -1mm to avoid pagebreak https://github.com/evnbr/bindery/issues/109
          //margin: { top: '10mm', inner: '20mm', outer: '10mm', bottom: '10mm' },
          margin: { top: '10mm', inner: '10mm', outer: '10mm', bottom: '10mm' },
        },
        //view: Bindery.View.PREVIEW, // LINEAR PREVIEW PRINT FLIPBOOK // note: cannot select text in PRINT view
        // "responsive" layout: set initial view from screen width
        view: (window.innerWidth < 960) ? Bindery.View.LINEAR : Bindery.View.PREVIEW,
      };
    }
    function hoverBothLinksStart(a) {
      var c = a.parentNode.className.split(' ')[2];
      //console.log(`+ hover .${c}`);
      Array.from(document.querySelectorAll(`.${c}`)).map(div => div.classList.add('hover'));
    }
    function hoverBothLinksEnd(a) {
      var c = a.parentNode.className.split(' ')[2];
      //console.log(`- hover .${c}`);
      Array.from(document.querySelectorAll(`.${c}`)).map(div => div.classList.remove('hover'));
    }
  </script>

</head>

<body>

<!-- note: dont indent body content -->

<script>document.body.classList.add('js-enabled')</script>

<noscript>This page works better with Javascript</noscript>

<article id="content">

<!-- note: dont indent article content -->
<!-- end of article: </article> -->

<!--
  print spreads with first side left
  workaround: add empty page before first page, then print sheets 2 and 3
  https://github.com/evnbr/bindery/issues/120
-->
<div class="para noprint">
  Please ignore this page, it is printed empty.
</div>
<div class="para noprint">
  It is a workaround to move the first page to the left side.
  See also:
</div>
<div class="para noprint">
  <a class="nofootnote" href="https://github.com/evnbr/bindery/issues/120">github.com/evnbr/bindery/issues/120</a>
</div>
<div class="pagebreak"></div>

<!-- page 1 -->

<div class="para">
  Dorfkultur ist Zukunft,
  aber ...
  <b>Wer sind meine Freunde?</b>
</div>

<div class="para">
  <b>Zuerst die schlechte Nachricht:</b>
  In den nächsten 5 Jahren
  werden 90 von 100 Menschen sterben.
  Das ist kein Zufall,
  sondern physikalisch notwendig,
  um 10 von 100 Menschen zu retten.

  Warum?
  Bevölkerungs-Explosion.
  Spätestens seit 200 Jahren
  explodiert die Zahl der Menschen weltweit.
  Es gibt genau zwei Wege,
  wie dieser Trend weitergehen kann:
  1. Weiteres Wachstum bis 10.000 Millionen und Kollaps zu Null.
  2. Stopp bei 8.000 Millionen und Kollaps zu 100 Millionen.
  <!-- TODO graph -->
  <!-- 2022: 7.9 billion people = 7.900 million people -->

  Wie werden die Menschen sterben?
  <!--
  Hungersnot, Impfschäden, Bürgerkrieg, Winter.
  -->
  Stromausfall, keine Heizung, kein Wasser, kein Essen ...
  drei Tage später herrscht Anarchie.

  Wann? Winter 2022.

  <!--
  Ausrede? "Russische Hacker".
  -->
</div>

<div class="para">
  <b>Glaubst du mir nicht?</b>
  Schön für dich.
  Scheinbar bist du blind für meine Wahrheiten
  und wirst den Rest von meinem Text abwehren
  als Geschwätz, Geschwurbel, Esoterik, Witz ...
  Bitte hör auf zu Lesen,
  und gib den Text weiter,
  oder lass ihn irgendwo liegen.
  Je mehr Menschen meinen Text lesen,
  desto früher wissen wir,
  ob meine Theorie richtig oder falsch ist.
</div>

<div class="para">
  <b>Aber jetzt die gute Nachricht.</b>
  Ich habe eine Idee (eine Vision, einen Traum)
  zum (Wieder-) Aufbau von <b>Dorfkultur:</b>
</div>

<!--
<div class="para">
  Teufelskind:
  Ein Problem, das ich lösen will ist das Teufelskind-Problem.
  einseitigkeit
  monopol
  diktatur
  schieflage
  ungerechtigkeit

  ignoranz gegen gefühle.
  alle menschen sollen gleich sein, sind es aber nicht.
  falsche erwartungen.
  dummheit vor allem in den wichtigen jobs: erzieher und lehrer.
  man muss dumm sein, um für dieses system zu arbeiten.
</div>
-->

<div class="para">
  Jedes Dorf hat 144 Menschen (Dunbar's Number) = 9 x 16 Menschen.
  Jedes Dorf hat Abstand zu den Nachbar-Dörfern
  (sonst wäre es ein Stadtteil).
  Jedes Dorf hat Selbstversorgung und Selbstverteidigung,
  also eine Mischung aus Ökodorf und Shaolin-Kloster,
  Permakultur und Kampfsport,
  Bauern und Soldaten.
</div>

<!--
  160 Menschen?
  eigentlich 150,
  aber 160 rechnet sich schöner.
  150 / 2 = 75. 150 / 4 = 37.5. (144 / 16 = 9.)
  160 / 2 = 80. 160 / 4 = 40. 160 / 16 = 10.

  Jedes Dorf hat genau 144 Menschen
  (Dunbar's Number, 12 x 12).
  <b>Gleichgewicht:</b>
  Die 144 Menschen sind im Gleichgewicht:
  = too specific. obere grenze ist ungenau.
  struktur ist bottom-up. ordnung von unten.

-->

<div class="para">
  <b>Gleichgewicht:</b>
  Jedes Dorf ist im Gleichgewicht:
  <div class="nowrap">72 Männer und 72 Frauen.</div>
  <div class="nowrap">72 Junge und 72 Alte.</div>
  <!-- (Koran: 72 Jungfrauen im Paradies für die Gerechten.) -->
  Also Gerechtigkeit bei der Verteilung von Geschlecht und Alter.
  Diese Gerechtigkeit kommt nicht von selber (Natur ist Zufall),
  sondern muss erarbeitet und gepflegt werden (Kultur).
  <!--
  "Natur ist ungerecht" weil "Gott würfelt".
  Menschen-Typen sind zufällig verteilt.
  -->
</div>

<div class="para">
  Beispiel:
  Dorf A hat 100 Männer und 44 Frauen und
  Dorf B hat 44 Männer und 100 Frauen.
  Der Abstand zum Gleichgewicht ist
  72 - 100 = -28 Männer für Dorf A und
  72 - 44 = +28 Männer für Dorf B.
  Lösung:
  Dorf A schickt 28 Männer zu Dorf B und
  Dorf B schickt 28 Frauen zu Dorf A.
  Ergebnis:
  Jedes Dorf hat 72 Männer und 72 Frauen.
</div>

<div class="para">
  Das gleiche gilt auch im kleinen Maßstab:
  Familie A hat 3 Männer und 1 Frau und
  Familie B hat 1 Mann und 3 Frauen.
  Lösung:
  Wir nehmen die zwei Kleinfamilien
  und bauen eine Großfamilie
  mit 4 Männer und 4 Frauen.
</div>

<div class="para">
  "Nein! Das bleibt hier alles so wies ist!"
  Okay Andreas.
  <!-- Okay <a href="https://youtu.be/gy39bTYYWs4">Andreas</a>. -->
  Schön für dich.
  Wenn ein Mensch sich wehrt gegen gut-gemeinte Ratschläge,
  dann hat das oft <b>Persönliche Gründe</b>.
  Dann zählt nicht "Was" einer sagt, sondern "Wer" etwas sagt.
  Zarathustra: "Ich bin nicht der Mund für diese Ohren."
  <b>Echte Freunde.</b>
  Geistige Nähe.
  <!--
    Geistige Freundschaft.

    "Freundschaft" klingt zu konkret.
    Kompatibilität ist etwas theoretisches,
    das "schon da" sein kann,
    aber auch "noch nicht da" sein kann.
    Auch wenn zwei menschen sich noch nie gesehen haben,
    können sie trotzdem kompatibel sein.
  -->
  Geist-Verwandtschaft.
  Soulmates.
  Sympathie.
  Kompatibilität.
</div>

<!-- tangent ...
<div class="para">
  "Dann muss man sich mehr Mühe geben."
  Nein.
  Wer etwas grundsätzlich falsch macht,
  der kann noch so viel "Mühe" reinstecken,
  und es bleibt trotzdem falsch.
  Was sind diese Grundsätze, die man falsch machen kann?
  Falsche Freunde und Falsche Feinde.
</div>
-->



<!-- force page break -->
<hr>



<!-- page 2 of 4 -->

<div class="para">
  Welche Menschen sind kompatibel?
  Dazu meine Kompatibilitäts-Landkarte:
  <!-- Dazu meine Kompatibilitäts-Tabelle für Persönlichkeitstypen: -->
</div>



<!-- i told ya ... code folding -->

<table style="margin-top:0.5em;" width="100%" class="compatibility-chart four-body-shapes"><tr>

  <td width="48%" class="compatibility-chart foldme">

    <script class="foldme" title="Compatibility Chart">

      if (false) { // generate svg

        /*
        // variant: four squares, one cross
        var grid_input = `
        F1 F2 F3 F4
        M2 M1 M4 M3
        F4 F3 F2 F1
        M3 M4 M1 M2
        `;
        */

        /*
        // variant: four squares, one cross
        var grid_input = `
        F1S F2L F3S F4L
        M2S M1L M4S M3L
        F4S F3L F2S F1L
        M3S M4L M1S M2L
        `;
        */

        // variant: four squares, one cross
        // rotated: parents top, children bottom
        var grid_input = `
        F1L M2L F4L M3L
        F2S M1S F3S M4S
        F3L M4L F2L M1L
        F4S M3S F1S M2S
        `;

        // variant: four crosses, one square
        // TODO?
        /*
        var grid_input = `
        M1L F3L M4L F2L
        M4S F2S M1S F3S
        M3L F1L M2L F4L
        M2S F4S M3S F1S
        `;
        */

        // circles
        var r = 60;
        //var r = 50;
        //var d = r / Math.sqrt(2); // exact value
        var d = r * 0.7; // rounded value

        var G = 200;

        var stroke_width = 2;

        var grid_circles_and_text = grid_input.trim().split(/\n[ \t]*/).map((line, y) => (
          line.split(' ').map((cell, x) => {
            return `
              <circle cx="${G * x + 100}" cy="${G * y + 100}" r="${r}" stroke-width="2" class="stroke nofill" />
              <text x="${G * x + 100}" y="${G * y + 100}" class="fillfg">${cell}</text>`;
          }).join('\n')
        )).join('\n')

        var tpl = `

        <!-`+`- start generated svg -`+`->

          <!-`+`- variant: four squares, one cross -`+`->
          <path class="stroke nofill" d="
            M ${500-d} ${500-d} L ${300+d} ${300+d}
            M ${500-d} ${300+d} L ${300+d} ${500-d}
            M 0 0 L ${100-d} ${100-d}
            M 800 800 L ${700+d} ${700+d}
            M 0 800 L ${100-d} ${700+d}
            M 800 0 L ${700+d} ${100-d}
            M 400 0 L ${300+d} ${100-d}
            M 400 0 L ${500-d} ${100-d}
            M 400 800 L ${300+d} ${700+d}
            M 400 800 L ${500-d} ${700+d}
            M 0 400 L ${100-d} ${300+d}
            M 0 400 L ${100-d} ${500-d}
            M 800 400 L ${700+d} ${300+d}
            M 800 400 L ${700+d} ${500-d}

            M ${100+r} 100 H ${300-r}
            M ${100+r} 300 H ${300-r}
            M 100 ${100+r} V ${300-r}
            M 300 ${100+r} V ${300-r}

            M ${100+r+400} 100 H ${(300+400)-r}
            M ${100+r+400} 300 H ${(300+400)-r}
            M ${100+400} ${100+r} V ${(300)-r}
            M ${300+400} ${100+r} V ${(300)-r}

            M ${100+r+400} ${100+400} H ${(300+400)-r}
            M ${100+r+400} ${300+400} H ${(300+400)-r}
            M ${100+400} ${100+r+400} V ${(300+400)-r}
            M ${300+400} ${100+r+400} V ${(300+400)-r}

            M ${100+r} ${100+400} H ${300-r}
            M ${100+r} ${300+400} H ${300-r}
            M ${100} ${100+r+400} V ${(300+400)-r}
            M ${300} ${100+r+400} V ${(300+400)-r}
          "/>


          <g dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="40">
            ${grid_circles_and_text}
          </g>

        <!`+`-- end generated svg --`+`>

            `;

        console.log(tpl); // print result

        // TODO bidirectional svg editor, like https://ravichugh.github.io/sketch-n-sketch/releases/icfp-2018-svg/

      } // end: generate svg

    </script>

    <svg class="foldme" title="Compatibility Chart"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      viewBox="0 0 800 800"
      >
      <style>
        svg { --fg: black; --bg: white; } /* light mode */
        @media screen {
          @media (prefers-color-scheme: dark) { 
            svg { --fg: white; --bg: black; } /* dark mode */
          }
          [data-darkreader-mode="dynamic"]
            svg { --fg: white; --bg: black; } /* dark mode */
        }
        @media screen {
          @media (prefers-color-scheme: dark) { 
            svg { --fg: #ffffff; --bg: #000000; } /* dark mode */
          }

          [data-darkreader-mode="dynamic"]
            svg { --fg: #ffffff; --bg: #000000; } /* dark mode */
        }

        .fig1 .stroke {
          stroke-width: 2;
          stroke: black; /* fallback when css variables are not supported */
          stroke: var(--fg);
        }

        .nofill {
          fill: none;
        }

        .fillbg {
          fill: white;
          fill: var(--bg);
        }

        .fillfg {
          fill: black;
          fill: var(--fg);
        }

      </style>

      <g class="fig1">



        <!-- start generated svg -->

          <!-- variant: four squares, one cross -->
          <path class="stroke nofill" d="
            M 458 458 L 342 342
            M 458 342 L 342 458
            M 0 0 L 58 58
            M 800 800 L 742 742
            M 0 800 L 58 742
            M 800 0 L 742 58
            M 400 0 L 342 58
            M 400 0 L 458 58
            M 400 800 L 342 742
            M 400 800 L 458 742
            M 0 400 L 58 342
            M 0 400 L 58 458
            M 800 400 L 742 342
            M 800 400 L 742 458

            M 160 100 H 240
            M 160 300 H 240
            M 100 160 V 240
            M 300 160 V 240

            M 560 100 H 640
            M 560 300 H 640
            M 500 160 V 240
            M 700 160 V 240

            M 560 500 H 640
            M 560 700 H 640
            M 500 560 V 640
            M 700 560 V 640

            M 160 500 H 240
            M 160 700 H 240
            M 100 560 V 640
            M 300 560 V 640
          "/>


          <g dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="40">
            
              <circle cx="100" cy="100" r="60" stroke-width="2" class="stroke nofill" />
              <text x="100" y="100" class="fillfg">F1L</text>

              <circle cx="300" cy="100" r="60" stroke-width="2" class="stroke nofill" />
              <text x="300" y="100" class="fillfg">M2L</text>

              <circle cx="500" cy="100" r="60" stroke-width="2" class="stroke nofill" />
              <text x="500" y="100" class="fillfg">F4L</text>

              <circle cx="700" cy="100" r="60" stroke-width="2" class="stroke nofill" />
              <text x="700" y="100" class="fillfg">M3L</text>

              <circle cx="100" cy="300" r="60" stroke-width="2" class="stroke nofill" />
              <text x="100" y="300" class="fillfg">F2S</text>

              <circle cx="300" cy="300" r="60" stroke-width="2" class="stroke nofill" />
              <text x="300" y="300" class="fillfg">M1S</text>

              <circle cx="500" cy="300" r="60" stroke-width="2" class="stroke nofill" />
              <text x="500" y="300" class="fillfg">F3S</text>

              <circle cx="700" cy="300" r="60" stroke-width="2" class="stroke nofill" />
              <text x="700" y="300" class="fillfg">M4S</text>

              <circle cx="100" cy="500" r="60" stroke-width="2" class="stroke nofill" />
              <text x="100" y="500" class="fillfg">F3L</text>

              <circle cx="300" cy="500" r="60" stroke-width="2" class="stroke nofill" />
              <text x="300" y="500" class="fillfg">M4L</text>

              <circle cx="500" cy="500" r="60" stroke-width="2" class="stroke nofill" />
              <text x="500" y="500" class="fillfg">F2L</text>

              <circle cx="700" cy="500" r="60" stroke-width="2" class="stroke nofill" />
              <text x="700" y="500" class="fillfg">M1L</text>

              <circle cx="100" cy="700" r="60" stroke-width="2" class="stroke nofill" />
              <text x="100" y="700" class="fillfg">F4S</text>

              <circle cx="300" cy="700" r="60" stroke-width="2" class="stroke nofill" />
              <text x="300" y="700" class="fillfg">M3S</text>

              <circle cx="500" cy="700" r="60" stroke-width="2" class="stroke nofill" />
              <text x="500" y="700" class="fillfg">F1S</text>

              <circle cx="700" cy="700" r="60" stroke-width="2" class="stroke nofill" />
              <text x="700" y="700" class="fillfg">M2S</text>
          </g>

        <!-- end generated svg -->

        </g>

    </svg>

  </td>

  <td width="4%" class="spacer"></td>

  <td width="48%" class="four-body-shapes foldme" style="vertical-align: top; ">
      
    <svg class="foldme" title="four body shapes"
      version="1.1"
      viewBox="0 0 142.5 82.46"
      style="margin-top:0.5em"
      xmlns="http://www.w3.org/2000/svg"
    >
    <style>
      svg { --fg: black; --bg: white; } /* light mode */
      @media screen {
        @media (prefers-color-scheme: dark) { 
          svg { --fg: white; --bg: black; } /* dark mode */
        }
        [data-darkreader-mode="dynamic"]
          svg { --fg: white; --bg: black; } /* dark mode */
      }

      .fig-four-body-types .stroke {
        stroke-width: .5;
        stroke: black; /* fallback when css variables are not supported */
        stroke: var(--fg);
      }

      .fig-four-body-types .dotstroke {
        stroke-width: 2;
        stroke-width: 4;
        stroke: black; /* fallback when css variables are not supported */
        stroke: var(--fg);
        stroke-dasharray: 10, 10;
      }

      .nofill {
        fill: none;
      }

      .fillbg {
        fill: white;
        fill: var(--bg);
      }

      .fillfg {
        fill: black;
        fill: var(--fg);
      }

      /* TODO fix xy values
      .fig-four-body-types text {
        dominant-baseline: middle; /* FIXME unknown css property? */
        text-anchor: middle;
        font-family: sans;
      }
      */
    </style>

    <!-- based on https://de.wikipedia.org/wiki/Datei:Bodyshapes.svg -->
    <!-- TODO bigger heads, bigger numbers -->
    <g class="fig-four-body-types" transform="translate(-31.42 -48.2)">
      <g class="fillfg" transform="translate(124.8 18.74)">
        <g>
          <path d="m-5.284 38.41s-0.2631 1.888-0.5905 2.403c-1.931 2.23-4.834 2.679-7.039 4.537-1.801 1.801-5.671 8.828-5.636 8.945 0.0351 0.1169 2.783 3.379 3.929 4.408 1.146 1.029 3.134 2.853 3.227 3.087 0.0935 0.2339 0.3508 3.064 0.3508 3.064s0.1871-2.689 0.1637-2.923c-0.0234-0.2339-2.268-2.76-3.508-3.999-1.239-1.239-4.069-3.531-4.069-3.625 2.342-3.45 4.335-7.622 7.928-9.986 2.651-1.149 5.655-2.524 5.244-5.911z" fill-rule="evenodd"/>
          <path d="m-10.08 49.66s-3.286 3.8-3.286 4.128c0 0.3274 3.122 5.601 3.099 5.589 0 0-3.905-5.227-3.905-5.741 0-0.5145 3.976-4.046 4.116-4.046m-0.2222 1.567c0.2253 3.324 2.556 4.916 2.556 7.808 0 3.037-3.433 4.767-3.433 10.77 0 6.408 3.718 14.22 3.718 14.22s-0.0935 8.021 0.421 10.64c0.5145 2.619 1.66 7.577 1.66 8.419s-0.0936 2.572-0.7484 3.601c-0.6548 1.029-1.403 1.777-1.403 1.777s1.777-3.181 1.777-4.303-1.231-7.468-1.871-9.354c-0.8653-2.549-0.1403-10.64-0.1403-10.64s-3.882-7.646-3.882-14.43c0-7.023 3.486-8.566 3.486-10.89 0-2.298-1.875-3.731-2.422-7.512"/>
          <path d="m-1.975 38.41s0.2631 1.888 0.5905 2.403c1.931 2.23 4.834 2.679 7.039 4.537 1.801 1.801 5.671 8.828 5.636 8.945-0.0351 0.1169-2.783 3.379-3.929 4.408-1.146 1.029-3.134 2.853-3.227 3.087-0.0936 0.2339-0.3508 3.064-0.3508 3.064s-0.1871-2.689-0.1637-2.923c0.0234-0.2339 2.268-2.76 3.508-3.999 1.239-1.239 4.069-3.531 4.069-3.625-2.342-3.45-4.335-7.622-7.928-9.986-2.651-1.149-5.655-2.524-5.244-5.911z" fill-rule="evenodd"/>
          <path d="m2.825 49.66s3.286 3.8 3.286 4.128c0 0.3274-3.122 5.601-3.099 5.589 0 0 3.905-5.227 3.905-5.741 0-0.5145-3.976-4.046-4.116-4.046m0.2222 1.567c-0.2253 3.324-2.556 4.916-2.556 7.808 0 3.037 3.433 4.767 3.433 10.77 0 6.408-3.718 14.22-3.718 14.22s0.0935 8.021-0.421 10.64c-0.5145 2.619-1.66 7.577-1.66 8.419 0 2.314 1.625 4.606 1.625 5.354 0 0.5342-0.7647 2.663-0.7647 2.663s0.9772-1.933 0.9772-2.573c0-1.916-1.463-3.577-1.463-4.369 0-2.082 1.231-7.468 1.871-9.354 0.8653-2.549 0.1403-10.64 0.1403-10.64s3.882-7.646 3.882-14.43c0-7.023-3.486-8.566-3.486-10.89 0-2.298 1.875-3.731 2.422-7.512"/>
          <g fill-rule="evenodd">
          <path d="m-3.623 109.1c0.4514-12.57 0.525-25.16-0.0525-37.73-0.3044 12.57-0.5393 25.16 0.0525 37.73z"/>
          <path d="m-5.068 29.95c-0.9724 0.6639-1.828 1.591-1.828 4.221 0 2.332 1.307 4.918 3.34 4.977 3.237 0.09449 3.007-5.163 3.007-5.163l-0.3471 0.0041s0.0267 0.7507-0.0433 1.266c-0.0892 0.6568-0.1618 3.345-2.567 3.463-1.503 0.07382-3.055-2.105-2.962-4.578 0.081-2.161 0.4294-2.835 0.8856-3.416 1.321-1.685 2.675-1.002 3.284-0.5498 0.4717 0.3507 0.9487 1.165 1.165 1.928 0.2786 0.9841 0.2384 1.887 0.2384 1.887l0.3471-0.0041s0.0808-0.6093-0.3153-2.099c-0.1746-0.6566-0.5754-1.508-1.42-2.001-1.217-0.7098-2.076-0.418-2.783 0.06468z"/>
          <path d="m-80.13 38.41s-0.2631 1.888-0.5905 2.403c-1.931 2.23-4.811 1.51-7.039 4.537-1.801 1.801-5.671 8.828-5.636 8.945 0.0351 0.1169 2.783 3.379 3.929 4.408 1.146 1.029 3.134 2.853 3.227 3.087 0.0935 0.2339 0.3508 3.064 0.3508 3.064s0.1871-2.689 0.1637-2.923c-0.0234-0.2339-2.268-2.76-3.508-3.999-1.239-1.239-4.069-3.531-4.069-3.625 2.342-3.45 4.335-7.902 7.928-10.27 3.797-1.617 5.655-2.243 5.244-5.63z"/>
          </g>
          <path d="m-84.82 49.65s-3.625 3.987-3.625 4.315c0 0.3274 2.923 5.426 2.9 5.414 0 0-3.695-4.934-3.695-5.449 0-0.5145 4.303-4.35 4.443-4.35m-1.193 1.321c0.2253 3.324 0.83 6.431 1.375 9.048 0.3684 1.768 0.7732 3.312 0.7732 4.548 0 1.139-0.4569 4.427-0.4413 5.405 0.0468 2.947 1.637 14.45 1.637 14.45s0.1403 7.624 0.6548 10.24c0.5145 2.619 1.731 8.606 1.731 9.448 0 0.8419-0.0935 2.572-0.7484 3.601-0.6548 1.029-1.403 1.777-1.403 1.777s1.777-3.18 1.777-4.303c0-1.123-1.38-8.466-1.918-10.38-0.5379-1.918-0.1871-10.29-0.1871-10.29s-2.152-11.79-2.152-14.78c0-0.7523 0.3645-3.913 0.3645-4.836-0.1376-1.377-0.2369-2.885-0.4561-4.25-0.4915-3.061-1.185-5.508-1.335-9.289"/>
          <path d="m-77.07 38.41s0.2631 1.888 0.5905 2.403c1.931 2.23 4.811 1.51 7.039 4.537 1.801 1.801 5.671 8.828 5.636 8.945-0.0351 0.1169-2.783 3.379-3.929 4.408-1.146 1.029-3.134 2.853-3.227 3.087-0.0935 0.2339-0.3508 3.064-0.3508 3.064s-0.1871-2.689-0.1637-2.923c0.0234-0.2339 2.268-2.76 3.508-3.999 1.239-1.239 4.069-3.531 4.069-3.625-2.342-3.45-4.335-7.902-7.928-10.27-3.797-1.617-5.655-2.243-5.244-5.63z" fill-rule="evenodd"/>
          <path d="m-71.99 49.65s3.625 3.987 3.625 4.315c0 0.3274-2.923 5.426-2.9 5.414 0 0 3.695-4.934 3.695-5.449 0-0.5145-4.303-4.35-4.443-4.35m1.193 1.321c-0.2253 3.324-0.83 6.431-1.375 9.048-0.3684 1.768-0.7732 3.312-0.7732 4.548 0 1.139 0.4569 4.427 0.4413 5.405-0.0468 2.947-1.637 14.45-1.637 14.45s-0.1403 7.624-0.6548 10.24c-0.5145 2.619-1.896 7.531-1.896 8.373 0 2.182 1.79 4.786 1.79 5.602 0 1.352-0.4985 3.034-0.4985 3.034s0.8663-1.437 0.8663-2.836c0-1.045-1.486-3.945-1.486-5.486s1.247-6.63 1.785-8.548c0.5379-1.918 0.1871-10.29 0.1871-10.29s2.152-11.79 2.152-14.78c0-0.7523-0.3645-3.913-0.3645-4.836 0.1376-1.377 0.2369-2.885 0.4561-4.25 0.4915-3.061 1.185-5.508 1.335-9.289"/>
          <path d="m-78.47 109.1c0.4514-12.57 0.5251-25.16-0.0525-37.73-0.3044 12.57-0.5393 25.16 0.0525 37.73z" fill-rule="evenodd"/>
          <path d="m-80.04 29.95c-0.9724 0.6639-1.828 1.591-1.828 4.221 0 2.332 1.307 4.918 3.34 4.977 3.237 0.09449 3.007-5.163 3.007-5.163l-0.3471 0.0041s0.0267 0.7507-0.0433 1.266c-0.0892 0.6568-0.1618 3.345-2.567 3.463-1.503 0.07382-3.055-2.105-2.962-4.578 0.081-2.161 0.4294-2.835 0.8856-3.416 1.321-1.685 2.675-1.002 3.284-0.5498 0.4717 0.3507 0.9487 1.165 1.165 1.928 0.2786 0.9841 0.2384 1.887 0.2384 1.887l0.3471-0.0041s0.0808-0.6093-0.3153-2.099c-0.1746-0.6566-0.5754-1.508-1.42-2.001-1.217-0.7098-2.076-0.418-2.783 0.06468z" fill-rule="evenodd"/>
        </g>
        <path class="nofill stroke" d="m-78.44 68.36-6.464-21.67 12.93-1e-6z"/>
        <g class="fillfg">
          <path d="m-42.98 38.41s-0.2631 1.888-0.5905 2.403c-1.931 2.23-4.834 2.679-7.039 4.537-1.801 1.801-5.671 8.828-5.636 8.945 0.0351 0.1169 2.783 3.379 3.929 4.408 1.146 1.029 3.134 2.853 3.227 3.087 0.0935 0.2339 0.3508 3.064 0.3508 3.064s0.1871-2.689 0.1637-2.923c-0.0234-0.2339-2.268-2.76-3.508-3.999-1.239-1.239-4.069-3.531-4.069-3.625 2.342-3.45 4.335-7.622 7.928-9.986 2.651-1.149 5.655-2.524 5.244-5.911z" fill-rule="evenodd"/>
          <path d="m-46.36 48.2s-4.747 5.262-4.747 5.589c0 0.3274 3.122 5.601 3.099 5.589 0 0-3.905-5.227-3.905-5.741 0-0.5145 5.437-5.507 5.578-5.507m-0.2806 0.3157c0.2253 3.324 0.662 5.144 0.662 7.762 0 3.49-1.507 6.915-2.033 8.29-0.5184 1.354-0.7375 4.427-0.722 5.405 0.0468 2.947 3.531 14.06 3.531 14.06s-0.0935 8.021 0.421 10.64c0.5145 2.619 1.777 8.396 1.777 9.238s-0.0935 2.572-0.7484 3.601c-0.6548 1.029-1.403 1.777-1.403 1.777s1.777-3.181 1.777-4.303-1.45-8.255-1.988-10.17c-0.5379-1.918-0.2105-10.31-0.2105-10.31s-3.812-11.76-3.812-14.76c0-0.7523 0.3848-3.69 0.6919-4.836 0.3087-1.152 2.187-5.136 2.187-8.577 0-3.108-0.0746-3.52-0.6217-7.301"/>
          <path d="m-39.37 38.41s0.2631 1.888 0.5905 2.403c1.931 2.23 4.834 2.679 7.039 4.537 1.801 1.801 5.671 8.828 5.636 8.945-0.0351 0.1169-2.783 3.379-3.929 4.408-1.146 1.029-3.134 2.853-3.227 3.087-0.0935 0.2339-0.3508 3.064-0.3508 3.064s-0.1871-2.689-0.1637-2.923c0.0234-0.2339 2.268-2.76 3.508-3.999 1.239-1.239 4.069-3.531 4.069-3.625-2.342-3.45-4.335-7.622-7.928-9.986-2.651-1.149-5.655-2.524-5.244-5.911z" fill-rule="evenodd"/>
          <path d="m-35.98 48.2s4.747 5.262 4.747 5.589c0 0.3274-3.122 5.601-3.099 5.589 0 0 3.905-5.227 3.905-5.741 0-0.5145-5.437-5.507-5.578-5.507m0.2806 0.3157c-0.2253 3.324-0.662 5.144-0.662 7.762 0 3.49 1.507 6.915 2.033 8.29 0.5184 1.354 0.7375 4.427 0.722 5.405-0.0468 2.947-3.531 14.06-3.531 14.06s0.0935 8.021-0.421 10.64c-0.5145 2.619-2.009 7.767-2.009 8.609 0 2.778 1.509 3.936 1.509 5.669 0 0.8011-0.482 2.935-0.482 2.935s1.015-1.758 1.015-2.926c0-1.198-1.585-3.366-1.585-5.495 0-1.123 1.599-6.734 2.137-8.652 0.5379-1.918 0.2105-10.31 0.2105-10.31s3.812-11.76 3.812-14.76c0-0.7523-0.3848-3.69-0.6919-4.836-0.3087-1.152-2.187-5.136-2.187-8.577 0-3.108 0.0746-3.52 0.6217-7.301"/>
          <path d="m-41.22 109.1c0.4514-12.57 0.525-25.16-0.0525-37.73-0.3044 12.57-0.5393 25.16 0.0525 37.73z" fill-rule="evenodd"/>
          <path d="m-42.72 29.95c-0.9724 0.6639-1.828 1.591-1.828 4.221 0 2.332 1.307 4.918 3.34 4.977 3.237 0.09449 3.007-5.163 3.007-5.163l-0.3471 0.0041s0.0267 0.7507-0.0433 1.266c-0.0892 0.6568-0.1618 3.345-2.567 3.463-1.503 0.07382-3.055-2.105-2.962-4.578 0.081-2.161 0.4294-2.835 0.8856-3.416 1.321-1.685 2.675-1.002 3.284-0.5498 0.4717 0.3507 0.9487 1.165 1.165 1.928 0.2786 0.9841 0.2384 1.887 0.2384 1.887l0.3471-0.0041s0.0808-0.6093-0.3153-2.099c-0.1746-0.6566-0.5754-1.508-1.42-2.001-1.217-0.7098-2.076-0.418-2.783 0.06468z" fill-rule="evenodd"/>
        </g>
        <path class="nofill stroke" d="m-41.09 46.69-6.464 21.67 12.93 1e-6z"/>
        <g class="fillfg" fill-rule="evenodd">
          <path d="m32.75 29.95c-0.9724 0.6639-1.828 1.591-1.828 4.221 0 2.332 1.307 4.918 3.34 4.977 3.237 0.09449 3.007-5.163 3.007-5.163l-0.3471 0.0041s0.0267 0.7507-0.0433 1.266c-0.0892 0.6568-0.1618 3.345-2.567 3.463-1.503 0.07382-3.055-2.105-2.962-4.578 0.081-2.161 0.4294-2.835 0.8856-3.416 1.321-1.685 2.675-1.002 3.284-0.5498 0.4717 0.3507 0.9487 1.165 1.165 1.928 0.2786 0.9841 0.2384 1.887 0.2384 1.887l0.3471-0.0041s0.0808-0.6093-0.3153-2.099c-0.1746-0.6566-0.5754-1.508-1.42-2.001-1.217-0.7098-2.076-0.418-2.783 0.06468z"/>
          <path d="m32.61 38.41s-0.2631 1.888-0.5905 2.403c-1.931 2.23-4.834 2.679-7.039 4.537-1.801 1.801-5.671 8.828-5.636 8.945 0.0351 0.1169 2.783 3.379 3.929 4.408 1.146 1.029 3.134 2.853 3.227 3.087 0.0935 0.2339 0.3508 3.064 0.3508 3.064s0.1871-2.689 0.1637-2.923c-0.0234-0.2339-2.268-2.76-3.508-3.999-1.239-1.239-4.069-3.531-4.069-3.625 2.342-3.45 4.335-7.622 7.928-9.986 2.651-1.149 5.655-2.524 5.244-5.911z"/>
          <path d="m28.34 60.31s-4.794-5.87-4.794-6.384c0-0.5145 4.303-4.35 4.443-4.35s1.497 4.934 1.497 8.115-1.029 6.876-0.9822 9.822c0.0468 2.947 1.637 16.84 1.637 16.84s0.1403 7.624 0.6548 10.24c0.5145 2.619 1.731 8.606 1.731 9.448 0 0.8419-0.0935 2.572-0.7484 3.601-0.6548 1.029-1.403 1.777-1.403 1.777s1.777-3.18 1.777-4.303c0-1.123-1.38-8.466-1.918-10.38-0.5379-1.918-0.1871-10.29-0.1871-10.29s-2.152-14.13-2.152-17.12 1.076-7.671 1.076-9.916-1.006-7.764-1.006-7.764-3.625 3.987-3.625 4.315c0 0.3274 4.022 6.361 3.999 6.349z"/>
          <path d="m40.46 60.31s4.794-5.87 4.794-6.384c0-0.5145-4.303-4.35-4.443-4.35-0.1403 0-1.497 4.934-1.497 8.115 0 3.181 1.029 6.876 0.9822 9.822-0.0468 2.947-1.637 16.84-1.637 16.84s-0.1403 7.624-0.6548 10.24c-0.5145 2.619-2.243 7.763-2.243 8.605 0 3.132 1.807 4.614 1.807 5.834 0 0.7514-0.697 2.885-0.697 2.885s1.034-1.604 1.034-2.766c0-1.293-1.373-2.596-1.373-5.72 0-1.123 1.496-6.779 2.033-8.697 0.5379-1.918 0.1871-10.29 0.1871-10.29s2.152-14.13 2.152-17.12-1.076-7.671-1.076-9.916c0-2.245 1.006-7.764 1.006-7.764s3.625 3.987 3.625 4.315c0 0.3274-4.022 6.361-3.999 6.349z"/>
          <path d="m35.84 38.41s0.2631 1.888 0.5905 2.403c1.931 2.23 4.834 2.679 7.039 4.537 1.801 1.801 5.671 8.828 5.636 8.945-0.0351 0.1169-2.783 3.379-3.929 4.408-1.146 1.029-3.134 2.853-3.227 3.087-0.0935 0.2339-0.3508 3.064-0.3508 3.064s-0.1871-2.689-0.1637-2.923c0.0234-0.2339 2.268-2.76 3.508-3.999 1.239-1.239 4.069-3.531 4.069-3.625-2.342-3.45-4.335-7.622-7.928-9.986-2.651-1.149-5.655-2.524-5.244-5.911z"/>
          <path d="m34.27 109.1c0.4514-12.57 0.5251-25.16-0.0525-37.73-0.3044 12.57-0.5393 25.16 0.0525 37.73z"/>
        </g>
        <path class="nofill stroke" d="m34.36 46.69-3.238 10.86 3.238 10.86 3.236-10.86z"/>
        <path class="nofill stroke" d="m-10.13 46.69 4.234 10.86-4.234 10.78h12.93l-4.231-10.83 4.231-10.81z"/>
      </g>
      <g class="fillfg" transform="translate(0 0)" font-family="sans-serif" font-size="5.644px">
        <text x="44.582211" y="54.929375">1</text>
        <text x="81.746994" y="54.929375">2</text>
        <text x="119.32879" y="54.929375">3</text>
        <text x="157.20084" y="54.929375">4</text>
      </g>
    </g>
    </svg>

    <div class="para" style="margin-top:1em">
      MF = Mann Frau.
    </div>

    <div class="para">
      1234 = vier Persönlichkeitstypen.
    </div>

    <div class="para">
      SL = Small Large = jung alt.
    </div>

  </td>

</tr></table>

<div class="para">
  Wie funktioniert diese Landkarte?
  Jeder Kreis ist ein Mensch,
  wir sehen also 16 Menschen von oben.
  Wenn zwei Menschen direkt nebeneinander sind,
  dann sind sie kompatibel.
  Wenn zwei Menschen weiter auseinander sind,
  dann sind sie Geist-Feinde (geistige Distanz)
  und brauchen <b>Gemeinsame Freunde</b>
  damit sie zusammen leben können.
  Wenn solche Geist-Feinde direkt zusammen kommen,
  dann gibt es immer wieder
  Misstrauen, Unglauben, Missverständnisse, Streit.
  <!--
    persönliche gründe: WEM glaube ich?
    = subjektiv, irrational, typen 12
    = menschen sind verschieden = gruppen haben innere struktur
    = gruppen haben ähnliche stukturen = naturgesetze regeln beziehungen

    sachliche gründe: WAS glaube ich?
    = objektiv, rational, typen 34
    = menschen sind gleich = gruppen sind homogen
    = aufbau von homogenen gesellschaften und "verschwörungen" (groupthink)
    = verschiedene gruppen können verschieden sein

    -> in welchem "kosmos" (scope, größenordnung) sieht man unterschiede?
  -->
</div>

<div class="para">
  Was passiert am Rand der Karte?
  Die Karte wird einfach wiederholt,
  wie ein Stempel, oder wie ein Kachel-Muster.
  Zeile 5 ist also:
  F1L M2L F4L M3L.
</div>

<div class="para">
  <b>Gruppendynamik:</b>
  Meine Landkarte ist aufgebaut aus Paaren und Vierer-Gruppen.
  Manche Vierer-Gruppen sind verbunden durch Vierecke ("Flusskreise")
  andere Vierer-Gruppen sind verbunden durch Kreuze ("Spannkreuze").
  Jede Vierer-Gruppe hat Sohn Mutter Tochter Vater = MS FL FS ML.
</div>

<div class="para">
  <b>Partnertausch:</b>
  Jede Vierer-Gruppe hat zwei Zustände.
  Die Frage ist: wer ist mit wem verbunden?
  In einem Zustand haben die Paare gleiches Alter (Eltern-Paar und Kinder-Paar),
  im anderen Zustand haben die Paare gleiches Geschlecht (Männer-Paar und Frauen-Paar).
  Die Vierer-Gruppen sollen immer hin-und-her "schwingen" zwischen diesen zwei Zuständen,
  Information fließt im Kreis.
</div>

<div class="para">
  Woher kommt diese Landkarte?
  Erfahrung und Symmetrie.
  Ich habe mir selber Psychologie beigebracht,
  vor allem Persönlichkeits-Psychologie,
  wo man Menschen "in Schubladen steckt".
  Ich habe also meine eigene Persönlichkeit rausgefunden
  (Typ 1 mit Subtyp 4)
  und habe meine Bekannten und Freunde typisiert ...
  und irgendwann bin ich auf diese Landkarte gekommen.
  Der Rest der Landkarte ergibt sich durch Symmetrie:
  Streifen-Muster und Sechseck-Muster.
</div>



<hr>



<!-- page 3 -->

<div class="para">
  Ich hoffe, diese Landkarte stimmt für alle Menschen.
  Wie können wir das testen?
  1. Beobachten: Wer ist wer, wer sind Freunde, wer sind Feinde?
  <div class="nowrap">2. Ändern:</div> Freunde verbinden, Feinde trennen.
</div>

<div class="para">
  Was bringt diese Theorie im Hier und Jetzt?
  <b>Aufbau von Großfamilien:</b>
  Man nimmt mehrere kleine Gruppen,
  und man baut zwei große Gruppen:
  Die "rechte" Gruppe realisiert meine Landkarte (16 oder 8 oder 4 Menschen),
  und in die "linke" Gruppe ("Kontrollgruppe") kommen alle,
  die bei der rechten Gruppe nicht reinpassen,
  weil manche Typen zu viel oder zu wenig sind.
</div>

<!--
  https://wikiislam.net/wiki/72_Jungfrauen

  koran zu hängetitten:
  Dies [Qur'an 78:33] bedeutet runde Brüste.
  Sie meinten damit, dass die Brüste dieser Mädchen
  voll gerundet und nicht hängend sein werden,
  weil sie Jungfrauen sein werden, gleichaltrig.

  Jungfrau = virgo = typ 2 = pear-shape = birnen-form
  = kleine titten + großer arsch.

  72 ist die Zahl der Houris,
  die in mehreren Hadithen bestätigt wird.
  Die Hadithen sind ein wichtiger Teil des Islam
  und bestimmte Muslime ignorieren sie,
  weil sie manchmal unangenehme Details über den Islam enthalten

  ... dass der Islam Frauen verspricht
  im Paradies für die "Gerechten" (also Muslime).

  Allah liebt die Gerechten.
  Allah liebt diejenigen die gütig und gerecht Handeln.
  Allah gebietet, Gerechtigkeit zu üben und Gutes zu tun.
  -> "gütig und gerecht" = helfen und fordern
  = aktiv und schlau = autorativer erziehungsstil = typ 1.
-->



<!-- force page break -->
<!--
  <hr>
-->



<!-- page 3 of 4 -->

<div class="para">
  <!-- Jetzt wirds verrückt: -->
  <b>Psychologie:</b>
  Auch unsere Gehirne haben Geschlecht und Alter.
  (Zwei-Faktoren-Modell von Persönlichkeit.
  Carl Jung, Hans Eysenck,
  Vier Elemente, Vier Temperamente ...)
  Meine Übersetzung:
  <div class="nowrap">Typen 14 = männliches Hirn.</div>
  <div class="nowrap">Typen 23 = weibliches Hirn.</div>
  <div class="nowrap">Typen 13 = junges Hirn.</div>
  <div class="nowrap">Typen 24 = altes Hirn.</div>
  <div class="nowrap">Typ 1234 = Sohn- Mutter- Tochter- Vater-Hirn.</div>
  <!--
  hier kein platz für zweifel/ablenkungen:

  (Andere Übersetzung:
  24 = weibliches Hirn,
  <div class="nowrap">13 = männliches Hirn</div>.
  Aber meine Übersetzung gefällt mir besser.)
  -->
</div>

<div class="para">
  In einem Dorf mit 144 Menschen
  müssen auch die Gehirne gleichmäßig verteilt sein:
  72 männliche und 72 weibliche Hirne,
  72 junge und 72 alte Hirne.
</div>

<!--
<div class="para">
  Gleichgewicht:
  Waage, Kompass, Griechisches Kreuz (+, Plus), Uhr, Kreis,
  Quadrat, Yin-Yang, Gerade Zahlen, Rechte Hand,
  Ganzheit, Perfektionismus, Gerechtigkeit, Bilanz.
  <!-xxxx-
    U+1F7A3	🞣	f0 9f 9e a3	MEDIUM GREEK CROSS
    U+271A	✚	e2 9c 9a	HEAVY GREEK CROSS
  -xxxxx->
</div>
<div class="para">
  Einseitigkeit:
  Römisches Kreuz (†, Schwert), Dreieck,
  Pyramide, Ungerade Zahlen, Linke Hand,
  3/4 Mehrheit.
  <!-xxxxx-
    U+271D	✝	e2 9c 9d	LATIN CROSS
  -xxxxxxxx->
</div>
-->

<div class="para">
  <b>Realisten und Idealisten:</b>
  Der Realist sagt:
  "Kinder können sich ihre Eltern nicht aussuchen."
  Der Idealist sagt:
  "Ja, aber warum nicht?
  Warum dürfen Kinder nur dann entscheiden wenns leicht geht,
  und müssen folgen wenns wichtig ist?"
  (Schönwetter-Freiheit)
  Dieser Konflikt setzt sich fort:
  "Schüler können sich ihre Lehrer nicht aussuchen."
  - "Ja, aber warum nicht?"
  - "Arbeiter können sich ihre Chefs nicht aussuchen."
  - "Ja, aber warum nicht?"
</div>

<!-- klein platz mehr auf dem zettel :(
<div class="para">
  <b>Sklaven und Herren:</b>
  Realisten sind bessere Sklaven,
  die sich immer anpassen (Gewohnheitstiere, Gutes Gewissen),
  die zufrieden sind mit dem was ist <div class="nowrap">(Status Quo)</div>,
  die alle wichtigen Entscheidungen delegieren,
  und unschuldig sein wollen (Zivilisten).
  Idealisten sind bessere Herren,
  die wichtigen Entscheidungen selber treffen wollen,
  die fühlen was sein soll (Gute Gefühle).
  Wer ist wer?
  Ich sage: Das ist angeboren und zufällig verteilt.
</div>

<div class="para">
  Problem?
  Alle Kinder werden zu Sklaven erzogen.
  Wer das Talent zum Herr hat,
  der wird in dieser Sklaven-Erziehung
  systematisch kaputt gemacht
  (unterfordert, vernachlässigt).
  "Pass dich an oder stirb"
  sagen die Sozial-Darwinisten.
</div>
-->

<div class="para">
  Lesen ist das Wichtigste, was man in der Schule lernt.
  Alles andere kann man dann selber lernen.
  Zum Lernen muss man <b>Aktiv Lesen:</b>
  Wichtige Wörter unterstreichen (es ist dein Text).
  Gedanken sofort aufschreiben.
  Nicht-verstandene Sätze nochmal lesen.
  Nicht-verstandene Wörter entschlüsseln.
  Regelmäßig Pausen machen (Bewegen oder Hinlegen).
  Aber auch Lesen ist subjektiv:
  Typen 24 wollen "linear" lesen,
  also von Aaah bis Zett genau ein Mal durchlesen.
  Typen 13 lesen eher chaotisch, lassen sich leichter ablenken.
  <!-- (ein Talent mit schlechtem Ruf.) -->
</div>

<!-- mostly useless
<div class="para">
  <b>Hirn und Hand:</b>
  Unsere linke Hand (3) geht zum rechten Hirn (4).
  Unser linkes Hirn (1) geht zur rechten Hand (2).
  Auch so kann man die vier Typen erklären.
  Jeder Typ hat sein "Ich" in einem dieser vier Körperteile.
  Die Hand ist hier ein Symbol für den Körper.
  Typen 13 sind aktiv, Typen 24 sind passiv.
  Typen 12 gehen den Right Hand Path
  (politisch Rechts, Qualität, Selektion, Ergebnis-Ethik),
  Typen 34 gehen den Left Hand Path
  (politisch Links, Quantität, Expansion, Gesinnungs-Ethik).
  Ja, Politik ist angeboren,
  aber trotzdem:
  Typen 34 glauben an eine "freie Wahl"
  (und heimliche Manipulation),
  während Typen 12 diese "Manipulation"
  öffentlich und direkt ausleben (Dominanzverhalten).
</div>
-->



<div class="para">
  <b>Warten auf Nichts:</b>
  Unser ganzes Leben lang werden wir trainiert auf Geduld.
  Sitzen bleiben, Konzentration, Durchhalten.
  Und dann?
  Dann ist ein neuer Tag, und wir sind wieder bei Null.
  Problem?
  Unsere wichtigen Probleme bleiben ungelöst,
  und wir verschwenden unsere Zeit mit einfachen Problemen.
  Wer will schon "die Welt retten",
  wenn er auch "Eins plus Eins" rechnen
  und dabei schwer beschäftigt aussehen kann.
  So kann man Arbeit meiden,
  in der Hoffnung:
  Irgendwer wirds schon machen
  (aber am Schluss machts keiner).
  Lösung?
  Delegieren an Freunde.
  Jeder braucht Freunde (Quantität),
  und jeder braucht die Vier Typen in seinem Freundeskreis (Qualität).
  Damit die Vier Typen kompatibel sind,
  müssen sie im richtigen Körper sein.
  <!--
    sein, wohnen, leben

    "ein gesunder geist wohnt in einem gesunden körper"
  -->
</div>



<hr>



<!-- page 4 of 4 -->

<!--
  Warten auf Morgen, Freitag, Sommer, Weihnachten ...
  warten auf "bessere Zeiten", die aber nie kommen.
-->

<!--
  anekdote:
  aaron swartz konnte schon vor der schule lesen,
  also im alter um 5 jahre.
  vielleicht auch deswegen die kategorie
  ELI5 = explain like i'm five.
  der kleine aaron war so fasziniert von seinen büchern,
  dass er sie genutzt hat,
  um andere kinder zu unterrichten.
  das alles vor der schule,
  im alter von 5 jahren.
  später wurde aaron ein mit-gründer von reddit.com
  (am anfang gut, heute nur noch scheisse.)
  aaron war gegen copyright.
  er hat private artikel gestohlen und öffentlich gemacht.
  artikel? wissenschaftliche studien. forschungs-protokolle.
  dafür wurde er vom system "fertig gemacht" und in den suizid getrieben.
  https://en.wikipedia.org/wiki/Aaron_Swartz
-->

<!-- #################################################### -->



<div class="para">
  <b>Vier Typen:</b>
  Die vier Persönlichkeitstypen
  in verschiedenen Sprachräumen.
  <br>
  Ideal: 2 Typen passen = Grundtyp und Subtyp
</div>

<!--
  the rename game: every "new" theory will invent new names for old things.
  all theories can be translated / traced back to the four elements and three modalities
  = twelve types of the zodiac in astrology.
  note: i mean NON-CALENDAR-astrology.
  calendar-astrology is obviously wrong.
-->

<!--
  more translations
  TODO copy to book appendix
  https://milahu.github.io/alchi/src/alchi-tables/alchi-tables.html
-->
<table class="four-basetypes" style="margin-top:0.5em">
  <tbody>
    <tr>
      <th>Typ Nummer</th>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr>
    <tr>
      <th>Hauptberuf</th>
      <td>Sohn</td>
      <td>Mutter</td>
      <td>Tochter</td>
      <td>Vater</td>
    </tr>
    <!--
      element ist schön weil kurz.
      "feuer mann" sagt sicht leichter als
      "typ eins mann" oder
      "vordenker mann" oder
      "handwerker mann" oder
      "linkshirn mann" oder
      ...
    -->
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Classical_element">Element</a></th>
      <td>Feuer</td>
      <td>Erde</td>
      <td>Luft</td>
      <td>Wasser</td>
    </tr>
    <tr>
      <th>Rolle</th>
      <td>Vordenker</td>
      <td>Nachmacher</td>
      <td>Vormacher</td>
      <td>Nachdenker</td>
    </tr>
    <!-- freie übersetzung
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Keirsey_Temperament_Sorter">David Keirsey</a></th>
      <td>Handwerker</td>
      <td>Händler</td>
      <td>Redner</td><!-xx- TODO better. Prediger? Redner? -xx->
      <td>Denker</td>
    </tr>
    -->
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Keirsey_Temperament_Sorter">David Keirsey</a></th>
      <td>Künstler</td><!-- praktisch kreativ -->
      <td>Versorger</td><!-- praktisch konservativ, Beschützer -->
      <td>Träumer</td><!-- theoretisch kreativ, Idealist, heisse luft -->
      <td>Verwalter</td><!-- theoretisch konservativ, kaltes wasser, Sachlich -->
    </tr>
    <!-- zu breit für tabelle
    <tr>
      <th>TODO</th>
      <td>praktisch kreativ</td>
      <td>praktisch konservativ</td>
      <td>theoretisch kreativ</td>
      <td>theoretisch konservativ</td>
    </tr>
    -->
    <!--
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Keirsey_Temperament_Sorter">David Keirsey</a></th>
      <td>Kreativ</td>
      <td>Konservativ</td>
      <td>Idealist</td>
      <td>Realist</td>
    </tr>
    -->
    <tr>
      <th>Interesse</th><!-- hauptberuf = grundtyp -->
      <td>Gerechtigkeit</td>
      <td>Sicherheit</td><!-- ritual, routine, wiederholung, tradition, Gewohnheit -->
      <td>Philosophie</td>
      <td>Technik</td>
    </tr>
    <!--
    <tr>
      <th><a class="nofootnote" href="https://libgen.rs/search.php?req=Robert+Moore+-+King+Warrior+Magician+Lover">Robert Moore</a></th>
      <td>Warrior</td>
      <td>Lover</td>
      <td>Magician</td>
      <td>King</td>
    </tr>
    -->
    <tr>
      <th><a class="nofootnote" href="https://libgen.rs/search.php?req=Robert+Moore+-+King+Warrior+Magician+Lover">Robert Moore</a></th>
      <td>Kämpfer</td>
      <td>Liebhaber</td>
      <td>Magier</td>
      <td>König</td>
    </tr>
    <!--
      <tr>
      <th><a class="nofootnote" href="https://libgen.rs/search.php?req=Carol+Tuttle+-+The+Child+Whisperer">Carol Tuttle</a></th>
      <td>Determined</td>
      <td>Sensitive</td>
      <td>Funny</td>
      <td>Serious</td>
    </tr>
    -->
    <tr>
      <th><a class="nofootnote" href="https://libgen.rs/search.php?req=Carol+Tuttle+-+The+Child+Whisperer">Carol Tuttle</a></th>
      <td>Willensstark</td>
      <!--
        rilke - panther:
        Der weiche Gang geschmeidig starker Schritte,
        der sich im allerkleinsten Kreise dreht,
        ist wie ein Tanz von Kraft um eine Mitte,
        in der betäubt ein großer Wille steht.
        "wille zur macht" ... "geborener könig" ... "dominant" ... "hausherr"
      -->
      <td>Sensibel</td>
      <td>Lustig</td>
      <td>Ernst</td>
    </tr>
    <tr>
      <th>Finger</th>
      <td>Mittelfinger</td>
      <td>Kleinfinger</td>
      <td>Ringfinger</td>
      <td>Zeigefinger</td>
    </tr>
    <tr>
      <th>Körperteil</th>
      <td>Linkes Hirn</td>
      <td>Rechte Hand</td>
      <td>Linke Hand</td>
      <td>Rechtes Hirn</td>
    </tr>
    <!--
      https://en.wikipedia.org/wiki/Playing_card_suit#Character_encodings
      french suit: Heart Pike Clover Tile
      french suit: Heart Spade Club Diamond
      german suit: Heart Leaf Bell Acorn
      swiss suit: Rose Shield Bell Acorn
      latin suit: Cup Sword Coin Club
      latin suit: Coppa Spada Denaro Bastone
      latin suit: Kelch Schwert Gold Schlagstock
      french white: ♡ ♤ ♧ ♢
      french black: ♥ ♠ ♣ ♦
    -->
    <tr>
      <th>Körperform</th>
      <td>Herz ♡</td>
      <td>Birne ♤</td>
      <td>Breit ♧</td>
      <td>Lang ♢</td>
    </tr>
    <tr>
      <th>Kleid oben</th>
      <td>Schwarz-Gelb</td>
      <td>Weiss-Blau</td>
      <td>Schwarz-Rot</td>
      <td>Weiss-Grün</td>
    </tr>
    <tr>
      <th>Kleid unten</th>
      <td>Weiss-Blau</td>
      <td>Schwarz-Gelb</td>
      <td>Schwarz-Grün</td>
      <td>Weiss-Rot</td>
    </tr>
    <tr>
      <th>Zylinder-Geometrie</th>
      <td>Oben</td>
      <td>Unten</td>
      <td>Aussen</td>
      <td>Innen</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Jungian_cognitive_functions">Carl Jung</a></th>
      <td>Ntuition</td>
      <td>Sensation</td>
      <td>Feeling</td>
      <td>Thinking</td>
    </tr>
    <!-- MBTI ist scheisse ... aber bekannt -->
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/MBTI">MBTI</a></th>
      <td>INTP</td>
      <td>ESFJ</td>
      <td>ENFP</td>
      <td>ISTJ</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Attachment_theory">Attachment Style</a></th>
      <td>Balanced</td>
      <td>Disorganized</td>
      <td>Coercive</td>
      <td>Avoidant</td>
    </tr>
    <tr>
      <th>↑ Aufregen</th><!-- Stress Response -->
      <td>früh</td>
      <td>spät</td>
      <td>früh</td>
      <td>spät</td>
    </tr>
    <tr>
      <th>↓ Beruhigen</th><!-- Stress Response -->
      <td>früh</td>
      <td>spät</td>
      <td>spät</td>
      <td>früh</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Parenting_styles">Parenting Style</a></th>
      <td>Authorative</td>
      <td>Neglectful</td>
      <td>Permissive</td>
      <td>Totalitarian</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Somatotype">William Sheldon</a></th>
      <td>mesomorph</td>
      <td>mesomorph</td>
      <td>endomorph</td>
      <td>ectomorph</td>
    </tr>
    <!-- note: typ 3 kann auch "Mittelgewicht" sein wenn sportlich aktiv, aber nie "Leichtgewicht" -->
    <tr>
      <th>Gewichtsklasse</th>
      <td>Mittelgewicht</td>
      <td>Mittelgewicht</td>
      <td>Schwergewicht</td>
      <td>Leichtgewicht</td>
    </tr>
    <tr>
      <th>Carbs+Fat+Protein</th>
      <td>50g+15g+35g</td>
      <td>50g+15g+35g</td>
      <td>30g+25g+45g</td>
      <td>60g+10g+30g</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://flowgenomeproject.com/flow-profile">Flow Profile</a></th>
      <td>Hard Charger</td>
      <td>Flow Goer</td>
      <td>Crowd Pleaser</td>
      <td>Deep Thinker</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://bc20questions.deloitte.com/">Business Chemistry</a></th>
      <td>Driver</td>
      <td>Guardian</td>
      <td>Pioneer</td>
      <td>Integrator</td>
    </tr>
    <!-- ähnlich: true colors: green blue orange gold = owl dolphin fox beaver -->
    <!--
    <tr>
      <th><a class="nofootnote" href="https://tobias-beck.com/wp-content/uploads/2017/09/EV-PT.pdf">Tobias Beck</a></th>
      <td>Hai</td>
      <td>Wal</td>
      <td>Delfin</td>
      <td>Eule</td>
    </tr>
    -->
    <!-- too broad, breaks layout
    <tr>
      <th><a class="nofootnote" href="http://bcn.boulder.co.us/~neal/uu/globalization/lerner-5C.html">circular paradigm</a></th>
      <td>liberal separatist</td>
      <td>conservative separatist</td>
      <td>liberal integrationist</td>
      <td>conservative integrationist</td>
    </tr>
    -->
    <tr>
      <th><a class="nofootnote" href="https://hiresuccess.com/help/understanding-the-4-personality-types">Hire Success</a></th>
      <td>Director</td>
      <td>Supporter</td>
      <td>Socializer</td>
      <td>Thinker</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Bartle_taxonomy_of_player_types">Richard Bartle</a></th>
      <td>Killer</td>
      <td>Explorer</td>
      <td>Socializer</td>
      <td>Achiever</td>
    </tr>
    <tr>
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/The_Satanic_Bible">Anton LaVey</a></th>
      <td>Satan</td><!-- justice -->
      <td>Belial</td><!-- ritual --><!-- belial, bella, bell, bell-shape, pear-shape -->
      <td>Lucifer</td><!-- philosophy -->
      <td>Leviathan</td><!-- technique, algorithm, step-by-step instruction -->
    </tr>
    <!-- nobody wants to be Fascist ...
    <tr>
      <!-x- Hans Eysenck -x->
      <th><a class="nofootnote" href="https://en.wikipedia.org/wiki/Political_spectrum#Hans_Eysenck">Political Spectrum</a></th>
      <td>Communist</td>
      <td>Capitalist</td>
      <td>Socialist</td>
      <td>Fascist</td>
    </tr>
    -->
    <tr>
      <th>Simpsons</th>
      <td>Marge</td>
      <td>Homer</td>
      <td>Bart</td>
      <td>Lisa</td>
    </tr>
    <tr>
      <th>South Park</th>
      <td>Kenny</td>
      <td>Stan</td>
      <td>Cartman</td>
      <td>Kyle</td>
    </tr>
    <!--
      American Dad: alternative translation: 1234 = Steve Stan Francine Hayley, weil Hayley: emotional, untreu, schlampig, idealistisch, pazifistisch, "liberal". stan ist mehr realistisch, pragmatisch, "konservativ".
      American Dad: alternative translation: 1234 = Steve Hayley Francine Stan, weil Stan = hightech?
      https://americandad.fandom.com/wiki/Hayley_Smith
    -->
    <tr>
      <th>American Dad</th>
      <td>Steve</td>
      <td>Stan</td>
      <td>Francine</td>
      <td>Hayley</td>
    </tr>
    <tr>
      <th>Harry Potter</th>
      <td>Gryffindor</td>
      <td>Hufflepuff</td>
      <td>Ravenclaw</td>
      <td>Slytherin</td>
    </tr>

  </tbody>
</table>


<!-- #################################################### -->

<div class="para">
  <b>Ziele:</b>
  Verbreitung und Experiment.
  <!--
    Werbung, Verbreitung, Vermehrung, Veröffentlichung
  -->
  Beide Ziele blockieren sich gegenseitig,
  also muss ich beides gleichzeitig machen.
  Jeder kann helfen, jeder darf helfen.
  Beispiel:
  1000 Flyer drucken (50 EUR) und irgendwo verteilen / schicken.
  <!--
    kosten:
    papier: 4 eur / 500 blatt (bei netto). durchschnitt 5 eur / 500 blatt = 1 cent pro blatt.
    laserdrucker: 1 bis 3 cent pro seite
    2 seiten
    1000 kopien
    1000 * 2 * (1...3 + 0.5) = 30...70 eur -> average 50 eur
  -->
</div>

<div class="para">
  <b>Ich:</b>
  <div class="nowrap">
  Milan Hauth,
  Jägerstraße 10,
  83308 Trostberg.
  </div>
  <div class="nowrap">Tel +49 151 7205 9978</div>,
  milahu@gmail.com,
  milahu@protonmail.com,
  @milahu:matrix.org
</div>

<div class="para">
  MIT License, Copyright (c) 2022 Milan Hauth.
  Das heisst:
  <div class="nowrap">Du darfst alles mit meinem Text machen,</div>
  <div class="nowrap">und ich gebe dir keine Garantie.</div>
</div>


<hr>

<p>
please ignore this page for printing
</p>



<!-- end of pamphlet -->



<!-- ######################################################################## -->



</article><!-- end #content -->



<script title="bindery.umd.js" class="foldme">
  // based on node_modules/bindery/dist/bindery.umd.js
  // source https://github.com/evnbr/bindery
  // license MIT License, Copyright (c) 2017 Evan Brooks

  /* bindery Bindery v2.3.6 */
  (function (global, factory) {
      typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
      typeof define === 'function' && define.amd ? define(factory) :
      (global = global || self, global.Bindery = factory());
  }(this, (function () { 'use strict';

      const BINDERY_VERSION = 'v2.3.6';
      const BINDERY_CLASS_PREFIX = 'bindery-';
        

      function ___$insertStyle(css) {
        if (!css) {
          return;
        }
        if (typeof window === 'undefined') {
          return;
        }

        var style = document.createElement('style');

        style.setAttribute('type', 'text/css');
        style.innerHTML = css;
        document.head.appendChild(style);
        return css;
      }

      const classPrefix = BINDERY_CLASS_PREFIX;
      var SheetLayout;
      (function (SheetLayout) {
          SheetLayout["PAGES"] = "pages";
          SheetLayout["SPREADS"] = "spreads";
          SheetLayout["BOOKLET"] = "booklet";
      })(SheetLayout || (SheetLayout = {}));
      var SheetMarks;
      (function (SheetMarks) {
          SheetMarks[SheetMarks["NONE"] = 0] = "NONE";
          SheetMarks[SheetMarks["CROP"] = 1] = "CROP";
          SheetMarks[SheetMarks["BLEED"] = 2] = "BLEED";
          SheetMarks[SheetMarks["BOTH"] = 3] = "BOTH";
      })(SheetMarks || (SheetMarks = {}));
      var SheetSize;
      (function (SheetSize) {
          SheetSize["AUTO"] = "auto";
          SheetSize["AUTO_BLEED"] = "auto-bleed";
          SheetSize["AUTO_MARKS"] = "auto-marks";
          SheetSize["LETTER_PORTRAIT"] = "letter-portrait";
          SheetSize["LETTER_LANDSCAPE"] = "letter-landscape";
          SheetSize["A4_PORTRAIT"] = "a4-portrait";
          SheetSize["A4_LANDSCAPE"] = "a4-landscape";
          SheetSize["A5_PORTRAIT"] = "a5-portrait";
          SheetSize["A5_LANDSCAPE"] = "a5-landscape";
      })(SheetSize || (SheetSize = {}));
      var ViewerMode;
      (function (ViewerMode) {
          ViewerMode["FLIPBOOK"] = "flipbook";
          ViewerMode["PREVIEW"] = "preview";
          ViewerMode["PRINT"] = "print";
          ViewerMode["LINEAR"] = "linear";
      })(ViewerMode || (ViewerMode = {}));

      //
      const prefixer = (str) => {
          if (str[0] === '.') {
              return `.${classPrefix}${str.substr(1)}`;
          }
          return `${classPrefix}${str}`;
      };

      const classes = {
          showBleed: 'show-bleed',
          showCrop: 'show-crop',
          showBleedMarks: 'show-bleed-marks',
          isViewing: 'viewing',
          viewPreview: 'view-preview',
          viewPrint: 'view-print',
          viewFlip: 'view-flip',
          viewLinear: 'view-linear',
          inProgress: 'in-progress',
          leftPage: 'left',
          rightPage: 'right',
          isOverflowing: 'is-overflowing',
          printSheet: 'print-sheet',
          sheetSpread: 'print-sheet-spread',
          sheetLeft: 'print-sheet-left',
          sheetRight: 'print-sheet-right',
          toNext: 'continues',
          fromPrev: 'continuation',
      };
      Object.keys(classes).forEach(k => {
          const key = k;
          const val = classes[key];
          classes[key] = prefixer(val);
      });
      const allModeClasses = [
          classes.viewPreview,
          classes.viewPrint,
          classes.viewFlip,
          classes.viewLinear,
      ];
      const classForMode = (mode) => {
          switch (mode) {
              case ViewerMode.PREVIEW:
                  return classes.viewPreview;
              case ViewerMode.PRINT:
                  return classes.viewPrint;
              case ViewerMode.FLIPBOOK:
                  return classes.viewFlip;
              case ViewerMode.LINEAR:
                  return classes.viewLinear;
              default:
                  throw Error(`Getting class for unknown mode: ${mode}`);
          }
      };

      const isElement = (node) => node.nodeType === Node.ELEMENT_NODE;
      const isFunc = (val) => typeof val === 'function';
      const isElementWrapper = (val) => (val === null || val === void 0 ? void 0 : val.element) && isElement(val.element);
      const h = (tagName, classNames, attrs, ...children) => {
          const el = document.createElement(tagName);
          if (classNames)
              el.className = classNames
                  .split('.')
                  .filter(txt => txt !== '')
                  .map(prefixer)
                  .join(' ');
          if (attrs)
              for (const k in attrs) {
                  // @ts-ignore TODO replace with hyperscript anyways
                  const v = attrs[k];
                  // @ts-ignore TODO replace with hyperscript anyways
                  if (isFunc(v))
                      el[k] = v;
                  else
                      el.setAttribute(k, v);
              }
          if (children) {
              el.append(...children.map(item => {
                  return isElementWrapper(item) ? item.element : item;
              }));
          }
          return el;
      };
      const div = (cls, ...children) => {
          return h('div', cls, {}, ...children);
      };
      const button = (cls, attrs, label) => {
          return h('button', cls, attrs, label);
      };
      const select = (cls, attrs, ...optionElements) => {
          return h('select', cls, attrs, ...optionElements);
      };
      const option = (attrs, label) => {
          return h('option', null, attrs, label);
      };

      const safeMeasure = (el, measureCallback) => {
          if (el.parentNode)
              return measureCallback();
          let measureArea = document.querySelector(prefixer('.measure-area'));
          if (!measureArea)
              measureArea = document.body.appendChild(div('.measure-area'));
          if (measureArea.firstElementChild !== el) {
              measureArea.innerHTML = '';
              measureArea.append(el);
          }
          const result = measureCallback();
          return result;
      };

      // Create stylesheet with id
      const addStylesheet = (id) => {
          const style = window.document.createElement('style');
          style.id = id;
          window.document.head.appendChild(style);
          return style;
      };
      // Fetch or create stylesheet with id
      const stylesheet = (id) => {
          var _a;
          return (_a = window.document.querySelector(`#${id}`)) !== null && _a !== void 0 ? _a : addStylesheet(id);
      };
      // Parse html from text
      const parseHTML = (text, selector) => {
          const wrapper = window.document.createElement('div');
          wrapper.innerHTML = text;
          return selector ? wrapper.querySelector(selector) : wrapper;
      };

      // https://github.com/moroshko/shallow-equal/blob/master/src/arrays.js
      const shallowEqual = (a, b) => {
          if (a === b)
              return true;
          if (!a || !b)
              return false;
          const len = a.length;
          if (b.length !== len) {
              return false;
          }
          for (let i = 0; i < len; i += 1) {
              if (a[i] !== b[i])
                  return false;
          }
          return true;
      };

      const throttleFrame = () => {
          let wasCalled = false;
          let queued;
          const inner = (func) => {
              if (wasCalled) {
                  queued = func;
                  return;
              }
              wasCalled = true;
              func();
              requestAnimationFrame(() => {
                  wasCalled = false;
                  if (queued) {
                      const queuedFunc = queued;
                      queued = undefined;
                      inner(queuedFunc);
                  }
              });
          };
          return inner;
      };
      const throttleTime = (ms) => {
          let wasCalled = false;
          let queued;
          const inner = (func) => {
              if (wasCalled) {
                  queued = func;
                  return;
              }
              wasCalled = true;
              func();
              setTimeout(() => {
                  wasCalled = false;
                  if (queued) {
                      const queuedFunc = queued;
                      queued = undefined;
                      inner(queuedFunc);
                  }
              }, ms);
          };
          return inner;
      };

      const formatAsRanges = (pageNumbers) => {
          let str = '';
          let prevNum = pageNumbers[0];
          let isInARange = false;
          const addFirst = (num) => {
              str += `${num}`;
          };
          const continueRange = () => {
              isInARange = true;
          };
          const endRange = (endNum) => {
              isInARange = false;
              str += `–${endNum}`;
          };
          const addComma = (num) => {
              str += `, ${num}`;
          };
          const endAndAdd = (endNum, num) => {
              endRange(endNum);
              addComma(num);
          };
          const addLast = (num, isAdjacent) => {
              if (isAdjacent)
                  endRange(num);
              else if (isInARange && !isAdjacent)
                  endAndAdd(prevNum, num);
              else
                  addComma(num);
          };
          pageNumbers.forEach((num, i) => {
              const isLast = i === pageNumbers.length - 1;
              const isAdjacent = num === prevNum + 1;
              if (i === 0)
                  addFirst(num);
              else if (isLast)
                  addLast(num, isAdjacent);
              else if (isAdjacent)
                  continueRange();
              else if (isInARange && !isAdjacent)
                  endAndAdd(prevNum, num);
              else
                  addComma(num);
              prevNum = num;
          });
          return str;
      };

      const cssNumberRegEx = /^([+-]?[0-9]+(.?[0-9]+)?)(px|in|cm|mm|pt|pc)$/;
      const isLength = (str) => cssNumberRegEx.test(str);
      const parseLength = (str) => {
          if (!isLength(str))
              throw Error(`Cannot parse css length from "${str}"`);
          const matches = str.match(cssNumberRegEx);
          if (!matches) {
              throw Error(`Failed to parse css length from "${str}"`);
          }
          return {
              val: Number(matches[1]),
              unit: matches[3],
          };
      };

      var defaultPageSetup = {
          bleed: '12pt',
          size: { width: '4in', height: '6in' },
          margin: {
              inner: '24pt',
              outer: '24pt',
              bottom: '40pt',
              top: '48pt',
          },
      };

      const letter = Object.freeze({ width: '8.5in', height: '11in' });
      const a4 = Object.freeze({ width: '210mm', height: '297mm' });
      const a5 = Object.freeze({ width: '148mm', height: '210mm' }); // TODO 210mm or 209mm? maybe need 209mm to avoid extra pagebreaks
  //    const a4 = Object.freeze({ width: '200mm', height: '287mm' });
      // Not a really reliable way to know this
      const supportsCustomPageSize = () => !!window.hasOwnProperty('chrome');
      class PageSetup {
          constructor(opts = {}, printOpts = {}) {
              var _a, _b, _c;
              this.size = (_a = opts.size) !== null && _a !== void 0 ? _a : defaultPageSetup.size;
              this.margin = (_b = opts.margin) !== null && _b !== void 0 ? _b : defaultPageSetup.margin;
              this.markLength = '12pt';
              this.paper = supportsCustomPageSize()
                  ? printOpts.paper || SheetSize.AUTO
                  : SheetSize.AUTO_MARKS;
              this.bleed = (_c = printOpts.bleed) !== null && _c !== void 0 ? _c : defaultPageSetup.bleed;
              this.printTwoUp =
                  !!printOpts.layout && printOpts.layout !== SheetLayout.PAGES;
          }
          get displaySize() {
              const width = this.printTwoUp ? this.spreadSize.width : this.size.width;
              const height = this.size.height;
              const bleed = this.bleed;
              return { width, height, bleed };
          }
          get sheetSize() {
              const width = this.printTwoUp ? this.spreadSize.width : this.size.width;
              const height = this.size.height;
              const doubleBleed = `2 * ${this.bleed}`;
              const doubleMarks = `${doubleBleed} + 2 * ${this.markLength}`;
              const singleMarks = `${this.bleed} + ${this.markLength}`;
              switch (this.paper) {
                  case SheetSize.AUTO:
                      return { width, height };
                  case SheetSize.AUTO_BLEED:
                      return {
                          width: `calc(${width} + ${this.printTwoUp ? doubleBleed : this.bleed})`,
                          height: `calc(${height} + ${doubleBleed})`,
                      };
                  case SheetSize.AUTO_MARKS:
                      return {
                          width: `calc(${width} + ${this.printTwoUp ? doubleMarks : singleMarks})`,
                          height: `calc(${height} + ${doubleMarks})`,
                      };
                  case SheetSize.LETTER_LANDSCAPE:
                      return { width: letter.height, height: letter.width };
                  case SheetSize.LETTER_PORTRAIT:
                      return letter;
                      case SheetSize.A4_PORTRAIT:
                      return a4;
                  case SheetSize.A4_LANDSCAPE:
                      return { width: a4.height, height: a4.width };
                  case SheetSize.A5_PORTRAIT:
                      return a5;
                  case SheetSize.A5_LANDSCAPE:
                      return { width: a5.height, height: a5.width };
                  default:
                      throw Error(`Can't get dimensions for unknown paper size: ${this.paper}`);
              }
          }
          get spreadSize() {
              const w = parseLength(this.size.width);
              console.log(`spreadSize.height = ${this.size.height}`)
              return {
                  height: this.size.height,
                  width: `${w.val * 2}${w.unit}`,
              };
          }
          updateStyleVars() {
              const page = this.size;
              const sheet = this.sheetSize;
              const cssVariables = {
                  'spread-width': this.spreadSize.width,
                  'page-width': page.width,
                  'page-height': page.height,
                  'sheet-width': sheet.width,
                  'sheet-height': sheet.height,
                  'margin-inner': this.margin.inner,
                  'margin-outer': this.margin.outer,
                  'margin-top': this.margin.top,
                  'margin-bottom': this.margin.bottom,
                  bleed: this.bleed,
                  'mark-length': this.markLength,
              };
              const cssStr = Object.entries(cssVariables)
                  .map(([k, v]) => {
                  return `--bindery-${k}: ${v};`;
              })
                  .join('');
              const rootRule = `:root { ${cssStr} }`;
              const pageRule = `@page { size: ${sheet.width} ${sheet.height}; }`;
              stylesheet('binderyPage').innerHTML = `${rootRule} ${pageRule}`;
          }
      }

      class Rule {
          constructor(options) {
              var _a;
              this.name = (_a = options === null || options === void 0 ? void 0 : options.name) !== null && _a !== void 0 ? _a : 'Unnamed Bindery Rule';
              this.selector = '';
              Object.keys(options).forEach(key => {
                  this[key] = options[key];
              });
          }
          setup() { }
      }

      const validateRuntimeOptions = (opts, validOpts) => {
          if (!validOpts)
              throw Error('Valid options not specified');
          Object.keys(opts).forEach(k => {
              var _a;
              if (!validOpts[k]) {
                  const setName = (_a = validOpts.name) !== null && _a !== void 0 ? _a : 'This option';
                  throw Error(`Unknown option in ${setName}: '${k}'`);
              }
              const val = opts[k];
              const type = validOpts[k];
              if (!type.check(val)) {
                  const optName = validOpts.name ? `${validOpts.name}.${k}` : k;
                  const valName = JSON.stringify(val);
                  throw Error(`Invalid value for '${optName}': ${valName} is not a ${type.name}.`);
              }
          });
          return true;
      };

      const isObj = (val) => typeof val === 'object';
      const isFunc$1 = (val) => typeof val === 'function';
      const isBool = (val) => typeof val === 'boolean';
      const isStr = (val) => typeof val === 'string';
      const isNum = (val) => typeof val === 'number';
      const isArr = (val) => Array.isArray(val);
      const hasProp = (obj, k) => Object.prototype.hasOwnProperty.call(obj, k);
      const hasSameKeys = (opts, required) => {
          const keys = Object.keys(required).filter(k => k !== 'name');
          return !keys.some(k => !hasProp(opts, k));
      };
      const isShape = (template) => {
          return (input) => {
              return isObj(input) && validateRuntimeOptions(input, template);
          };
      };
      const isShapeExact = (template) => {
          return (input) => {
              return (isObj(input) &&
                  hasSameKeys(input, template) &&
                  validateRuntimeOptions(input, template));
          };
      };
      const isEnum = (cases) => {
          return (str) => {
              return cases.includes(str);
          };
      };
      const lengthChecker = {
          name: 'length (string with absolute units)',
          check: isLength,
      };
      const RuntimeTypes = {
          any: {
              name: 'any',
              check: () => true,
          },
          enum(...cases) {
              return {
                  name: `(${cases.map(c => `"${c}"`).join(' | ')})`,
                  check: isEnum(cases),
              };
          },
          shapeExact: (template) => ({
              name: `exactly ({${Object.keys(template).join(', ')}})`,
              check: isShapeExact(template),
          }),
          shape: (template) => ({
              name: `shape ({${Object.keys(template).join(', ')}})`,
              check: isShape(template),
          }),
          string: {
              name: 'string',
              check: isStr,
          },
          length: lengthChecker,
          number: {
              name: 'number',
              check: isNum,
          },
          bool: {
              name: 'bool',
              check: isBool,
          },
          func: {
              name: 'func',
              check: isFunc$1,
          },
          obj: {
              name: 'object',
              check: isObj,
          },
          array: {
              name: 'array',
              check: isArr,
          },
          margin: {
              name: 'margin ({ top, inner, outer, bottom })',
              check: isShapeExact({
                  name: 'margin',
                  top: lengthChecker,
                  inner: lengthChecker,
                  outer: lengthChecker,
                  bottom: lengthChecker,
              }),
          },
          size: {
              name: 'size ({ width, height })',
              check: isShapeExact({
                  name: 'size',
                  width: lengthChecker,
                  height: lengthChecker,
              }),
          },
      };

      class Counter extends Rule {
          constructor(options) {
              var _a, _b, _c;
              super(options);
              this.selector = '*';
              this.counterValue = 0;
              this.incrementEl = (_a = options.incrementEl) !== null && _a !== void 0 ? _a : '';
              this.resetEl = (_b = options.resetEl) !== null && _b !== void 0 ? _b : '';
              this.replaceEl = (_c = options.replaceEl) !== null && _c !== void 0 ? _c : '';
              validateRuntimeOptions(options, {
                  name: 'Counter',
                  replaceEl: RuntimeTypes.string,
                  resetEl: RuntimeTypes.string,
                  incrementEl: RuntimeTypes.string,
                  replace: RuntimeTypes.func,
              });
          }
          setup() {
              this.counterValue = 0;
          }
          beforeAdd(el) {
              if (this.incrementEl.length && el.matches(this.incrementEl))
                  this.counterValue += 1;
              if (this.resetEl.length && el.matches(this.resetEl))
                  this.counterValue = 0;
              if (this.replaceEl.length && el.matches(this.replaceEl))
                  return this.createReplacement(el);
              return el;
          }
          createReplacement(element) {
              return this.replace(element, this.counterValue);
          }
          replace(element, counterValue) {
              element.textContent = `${counterValue}`;
              return element;
          }
      }

      class OutOfFlow extends Rule {
          constructor(options) {
              super(options);
              this.name = 'Out of Flow';
          }
          createOutOfFlowPages(elmt, book, makeNewPage) {
              throw Error('createOutOfFlowPages must be overridden');
          }
          beforeAdd(elmt) {
              // Avoid breaking inside this element. Once it's completely added,
              // it will moved onto the background layer.
              // TODO: this should be handled inside regionize
              elmt.setAttribute('data-ignore-overflow', 'true');
              return elmt;
          }
          afterAdd(elmt, book, continueOnNewPage, makeNewPage) {
              this.createOutOfFlowPages(elmt, book, makeNewPage);
              // Catches cases when we didn't need to create a new page. but unclear
              if (this.continue !== 'same' || book.currentPage.hasOutOfFlowContent) {
                  continueOnNewPage();
                  if (this.continue === 'left' || this.continue === 'right') {
                      book.currentPage.setPreference(this.continue);
                  }
              }
              return elmt;
          }
      }

      class FullBleedSpread extends OutOfFlow {
          constructor(options) {
              var _a, _b;
              options.continue = (_a = options.continue) !== null && _a !== void 0 ? _a : 'same';
              options.rotate = (_b = options.rotate) !== null && _b !== void 0 ? _b : 'none';
              super(options);
              validateRuntimeOptions(options, {
                  name: 'FullBleedSpread',
                  selector: RuntimeTypes.string,
                  continue: RuntimeTypes.enum('next', 'same', 'left', 'right'),
                  rotate: RuntimeTypes.enum('none', 'clockwise', 'counterclockwise'),
              });
          }
          createOutOfFlowPages(elmt, book, makeNewPage) {
              if (!!elmt.parentNode) {
                  elmt.parentNode.removeChild(elmt);
              }
              let leftPage;
              if (book.currentPage.isEmpty) {
                  leftPage = book.currentPage;
              }
              else {
                  leftPage = makeNewPage();
                  book.addPage(leftPage);
              }
              const rightPage = makeNewPage();
              book.addPage(rightPage);
              if (this.rotate !== 'none') {
                  [leftPage, rightPage].forEach(page => {
                      const rotateContainer = div(`.rotate-container.spread-size-rotated.rotate-spread-${this.rotate}`);
                      rotateContainer.append(page.background);
                      page.element.append(rotateContainer);
                  });
              }
              leftPage.background.append(elmt);
              leftPage.element.classList.add(prefixer('spread'));
              leftPage.setPreference('left');
              leftPage.isOutOfFlow = this.continue === 'same';
              leftPage.avoidReorder = true;
              leftPage.hasOutOfFlowContent = true;
              rightPage.background.append(elmt.cloneNode(true));
              rightPage.element.classList.add(prefixer('spread'));
              rightPage.setPreference('right');
              rightPage.isOutOfFlow = this.continue === 'same';
              rightPage.avoidReorder = true;
              rightPage.hasOutOfFlowContent = true;
          }
      }

      class FullBleedPage extends OutOfFlow {
          constructor(options) {
              var _a, _b;
              options.continue = (_a = options.continue) !== null && _a !== void 0 ? _a : 'same';
              options.rotate = (_b = options.rotate) !== null && _b !== void 0 ? _b : 'none';
              super(options);
              validateRuntimeOptions(options, {
                  name: 'FullBleedPage',
                  selector: RuntimeTypes.string,
                  continue: RuntimeTypes.enum('next', 'same', 'left', 'right'),
                  rotate: RuntimeTypes.enum('none', 'inward', 'outward', 'clockwise', 'counterclockwise'),
              });
          }
          createOutOfFlowPages(elmt, book, makeNewPage) {
              if (elmt.parentNode) {
                  elmt.parentNode.removeChild(elmt);
              }
              let newPage;
              if (book.currentPage.isEmpty) {
                  newPage = book.currentPage;
              }
              else {
                  newPage = makeNewPage();
                  book.addPage(newPage);
              }
              if (this.rotate !== 'none') {
                  const rotateContainer = div(`.rotate-container.page-size-rotated.rotate-${this.rotate}`);
                  rotateContainer.appendChild(newPage.background);
                  newPage.element.appendChild(rotateContainer);
              }
              newPage.background.appendChild(elmt);
              newPage.hasOutOfFlowContent = true;
          }
      }

      class Replace extends Rule {
          constructor(options) {
              super(options);
              this.name = 'Replace';
          }
          afterAdd(element, book, continueOnNewPage, makeNewPage, overflowCallback) {
              const parent = element.parentNode;
              if (!parent) {
                  console.error(element);
                  throw Error(`Bindery.Replace({ selector: '${this.selector}' }).afterAdd called on element that hasn't been added.`);
              }
              const defensiveClone = element.cloneNode(true);
              const replacement = this.createReplacement(book, defensiveClone);
              parent.replaceChild(replacement, element);
              if (book.currentPage.hasOverflowed()) {
                  parent.replaceChild(element, replacement);
                  return overflowCallback(element);
              }
              return replacement;
          }
          createReplacement(book, element) {
              return this.replace(element);
          }
          replace(element, info) {
              element.insertAdjacentHTML('beforeend', '<sup class="bindery-sup">Default Replacement</sup>');
              return element;
          }
      }

      class Footnote extends Replace {
          constructor(options) {
              super(options);
              validateRuntimeOptions(options, {
                  name: 'Footnote',
                  selector: RuntimeTypes.string,
                  replace: RuntimeTypes.func,
                  render: RuntimeTypes.func,
              });
          }
          afterAdd(element, book, continueOnNewPage, makeNewPage, overflowCallback) {
              const number = book.currentPage.footer.children.length + 1;
              const footnote = div('.footnote');
              const contents = this.render(element, number);
              if (contents instanceof HTMLElement)
                  footnote.appendChild(contents);
              else
                  footnote.innerHTML = contents;
              book.currentPage.footer.appendChild(footnote);
              return super.afterAdd(element, book, continueOnNewPage, makeNewPage, (overflowEl) => {
                  book.currentPage.footer.removeChild(footnote);
                  return overflowCallback(overflowEl);
              });
          }
          createReplacement(book, element) {
              const number = book.currentPage.footer.children.length;
              return this.replace(element, number);
          }
          replace(element, number) {
              //element.insertAdjacentHTML('beforeend', `<sup class="bindery-sup">${number}</sup>`);
              element.insertAdjacentHTML('beforeend', ` <span class="bindery-sup">[${number}]</span>`);
              return element;
          }
          render(originalElement, number) {
              return `<sup>${number}</sup> Default footnote (<a href='/bindery/docs/#footnote'>Learn how to change it</a>)`;
          }
      }

      /* bindery Regionize v0.1.7 */
      const div$1 = (cls) => {
          const el = document.createElement('div');
          el.classList.add(cls);
          return el;
      };
      class Region {
          constructor(el) {
              this.suppressErrors = false;
              this.element = el;
              this.content = div$1('region-content');
              this.content.style.position = 'relative';
              this.element.appendChild(this.content);
              this.path = [];
          }
          setPath(newPath) {
              this.path = newPath;
              if (newPath.length > 0)
                  this.content.appendChild(newPath[0]);
          }
          get currentElement() {
              const len = this.path.length;
              if (len > 0)
                  return this.path[len - 1];
              return this.content;
          }
          isEmpty() {
              const el = this.content;
              if (el.textContent === null)
                  return true;
              return el.textContent.trim() === '' && el.offsetHeight < 2;
          }
          isReasonableSize() {
              const box = this.element.getBoundingClientRect();
              return box.height > 100 && box.width > 100; // TODO: Number is arbitrary
          }
          overflowAmount() {
              const contentH = this.content.offsetHeight;
              const boxH = this.element.offsetHeight;
              if (boxH === 0)
                  throw Error('Regionizer: Trying to flow into an element with zero height.');
              return contentH - boxH;
          }
          hasOverflowed() {
              return this.overflowAmount() > -5;
          }
      }

      const isTextNode = (node) => {
          return node.nodeType === Node.TEXT_NODE;
      };
      const isElement$1 = (node) => {
          return node.nodeType === Node.ELEMENT_NODE;
      };
      const isScript = (node) => {
          return node.tagName === 'SCRIPT';
      };
      const isImage = (node) => {
          return node.tagName === 'IMG';
      };
      const isUnloadedImage = (node) => {
          return isImage(node) && !node.naturalWidth;
      };
      const isContentElement = (node) => {
          return isElement$1(node) && !isScript(node);
      };

      const MAX_TIME = 30; // ms
      const rAF = () => new Promise(resolve => {
          requestAnimationFrame(t => resolve(t));
      });
      let lastYieldTime = 0;
      const shouldYield = () => {
          const timeSinceYield = performance.now() - lastYieldTime;
          return timeSinceYield > MAX_TIME;
      };
      const yieldIfNecessary = async () => {
          if (shouldYield())
              lastYieldTime = await rAF();
      };

      const overflowAttr = 'data-ignore-overflow';
      // Walk up the tree to see if we are within
      // an overflow-ignoring node
      const ignoreOverflow = (element) => {
          if (element.hasAttribute(overflowAttr))
              return true;
          if (element.parentElement)
              return ignoreOverflow(element.parentElement);
          return false;
      };

      const SPACE = ' ';
      const nextWordEnd = (text, startIndex) => {
          let newIndex = startIndex + 1;
          while (newIndex < text.length && text.charAt(newIndex) !== SPACE) {
              newIndex += 1;
          }
          return newIndex;
      };
      const previousWordEnd = (text, startIndex) => {
          let newIndex = startIndex;
          if (text.charAt(newIndex) === SPACE) {
              newIndex -= 1;
          }
          while (text.charAt(newIndex) !== SPACE && newIndex > 0) {
              newIndex -= 1;
          }
          return newIndex;
      };

      const createTextNode = (text) => document.createTextNode(text);
      // Try adding a text node in one go.
      // Returns true if all the text fits, false if none fits.
      const addInOneGo = async (textNode, container, hasOverflowed) => {
          container.appendChild(textNode);
          const success = !hasOverflowed();
          if (!success)
              container.removeChild(textNode);
          await yieldIfNecessary();
          return { completed: success };
      };
      // Incrementally add words to the container until it just barely doesnt
      // overflow. Returns a remainder textNode for remaining text.
      const fillWordsUntilOverflow = async (textNode, container, hasOverflowed) => {
          const originalText = textNode.nodeValue || '';
          container.appendChild(textNode);
          if (!hasOverflowed() || ignoreOverflow(container)) {
              // The whole thing fits
              return { completed: true };
          }
          // Clear the node
          let proposedEnd = 0;
          textNode.nodeValue = originalText.substr(0, proposedEnd);
          while (!hasOverflowed() && proposedEnd < originalText.length) {
              // Reveal the next word
              proposedEnd = nextWordEnd(originalText, proposedEnd);
              if (proposedEnd < originalText.length) {
                  textNode.nodeValue = originalText.substr(0, proposedEnd);
                  await yieldIfNecessary();
              }
          }
          // Back out to word boundary
          const wordEnd = previousWordEnd(originalText, proposedEnd);
          if (wordEnd < 1) {
              // We didn't even add a complete word, don't add node
              textNode.nodeValue = originalText;
              container.removeChild(textNode);
              return { completed: false };
          }
          // trim text to word
          const fittingText = originalText.substr(0, wordEnd);
          const overflowingText = originalText.substr(wordEnd);
          textNode.nodeValue = fittingText;
          // Create a new text node for the next flow box
          return {
              completed: true,
              remainder: createTextNode(overflowingText),
          };
      };
      // Fills text across multiple elements by requesting a continuation
      // once the current element overflows
      const fillWords = async (textNode, container, getNextContainer, hasOverflowed) => {
          const textLayout = await fillWordsUntilOverflow(textNode, container, hasOverflowed);
          if (textLayout.remainder) {
              const nextContainer = getNextContainer();
              return fillWords(textLayout.remainder, nextContainer, getNextContainer, hasOverflowed);
          }
          return textLayout;
      };

      // Shifts this element to the next page. If any of its
      // ancestors cannot be split across page, it will
      // step up the tree to find the first ancestor
      // that can be split, and move all of that descendants
      // to the next page.
      const tryInNextRegion = (region, makeNextRegion, canSplit) => {
          if (region.path.length <= 1) {
              throw Error('Regionize: Attempting to move the top-level element');
          }
          const startLength = region.path.length;
          // So this node won't get cloned. TODO: this is unclear
          const elementToMove = region.path.pop();
          // find the nearest splittable parent
          let nearestMoveableElement = elementToMove;
          const pathToRestore = [];
          while (region.path.length > 1 && !canSplit(region.currentElement)) {
              nearestMoveableElement = region.path.pop();
              pathToRestore.unshift(nearestMoveableElement);
          }
          // Once a node is moved to a new page, it should no longer trigger another
          // move. otherwise tall elements will endlessly get shifted to the next page
          nearestMoveableElement.setAttribute('data-regionize-did-move', 'true');
          const parent = nearestMoveableElement.parentNode;
          parent.removeChild(nearestMoveableElement);
          // If the nearest ancestor would be empty without this node,
          // move it to the next page too.
          if (region.path.length > 1 &&
              region.currentElement.textContent.trim() === '') {
              parent.appendChild(nearestMoveableElement);
              nearestMoveableElement = region.path.pop();
              pathToRestore.unshift(nearestMoveableElement);
              nearestMoveableElement.parentNode.removeChild(nearestMoveableElement);
          }
          let nextRegion;
          if (!region.isEmpty()) {
              if (region.hasOverflowed()) {
                  // Recovery failed, maybe the box contains a large
                  // unsplittable element.
                  region.suppressErrors = true;
              }
              nextRegion = makeNextRegion();
          }
          else {
              // If the page is empty when this node is removed,
              // then it won't help to move it to the next page.
              // Instead continue here until the node is done.
              nextRegion = region;
          }
          // append moved node as first in new page
          nextRegion.currentElement.appendChild(nearestMoveableElement);
          // restore subpath
          pathToRestore.forEach(r => nextRegion.path.push(r));
          nextRegion.path.push(elementToMove);
          if (startLength !== nextRegion.path.length) {
              throw Error('Regionize: Restored path depth does not match original path depth');
          }
      };

      // The path is an array of nested elments,
      // for example .content > article > p > a).
      //
      // It's shallowly cloned every time we move to the next page,
      // to create the illusion that nodes are continuing from page
      // to page.
      //
      // The transition can be customized by setting a Split rule,
      // which lets you add classes to the original and cloned element
      // to customize styling.
      const clone = (el, withChildren) => {
          return el.cloneNode(withChildren);
      };
      const shallowClone = (el) => clone(el, false);
      const deepClone = (el) => clone(el, true);
      const clonePath = (oldPath, applyRules) => {
          const newPath = [];
          const deepCloneWithRules = (el) => {
              const clone = deepClone(el); // could be th > h3 > span;
              applyRules(el, clone);
              return clone;
          };
          for (let i = oldPath.length - 1; i >= 0; i -= 1) {
              const original = oldPath[i];
              const clone = shallowClone(original);
              const nextChild = oldPath[i + 1];
              clone.innerHTML = '';
              applyRules(original, clone, nextChild, deepCloneWithRules);
              if (i < oldPath.length - 1)
                  clone.appendChild(newPath[i + 1]);
              newPath[i] = clone;
          }
          return newPath;
      };

      // Polls every 10ms for image.naturalWidth
      // or an error event.
      //
      // Note: Doesn't ever reject, since missing images
      // shouldn't prevent layout from resolving
      const wait10 = () => new Promise(resolve => {
          setTimeout(() => {
              resolve();
          }, 10);
      });
      const ensureImageLoaded = async (image) => {
          const imgStart = performance.now();
          let failed = false;
          image.addEventListener('error', () => {
              failed = true;
          });
          image.src = image.src; // re-trigger error if already failed
          while (!image.naturalWidth && !failed) {
              await wait10();
          }
          return performance.now() - imgStart;
      };

      const preserveNumbering = (original, clone, nextChild) => {
          // restart numbering
          let prevStart = 1;
          if (original.hasAttribute('start')) {
              // the OL is also a continuation
              prevStart = parseInt(original.getAttribute('start') || '', 10);
          }
          if (nextChild && nextChild.tagName === 'LI') {
              // the first list item is a continuation
              prevStart -= 1;
          }
          const prevCount = original.children.length;
          const newStart = prevStart + prevCount;
          clone.setAttribute('start', `${newStart}`);
      };

      const preserveTableColumns = (original, clone, nextChild, deepClone) => {
          const columns = [...original.children];
          const currentIndex = columns.indexOf(nextChild);
          for (let i = 0; i < currentIndex; i += 1) {
              const origCol = columns[i];
              if (origCol) {
                  const clonedCol = deepClone(origCol);
                  clone.appendChild(clonedCol);
              }
          }
      };

      const noop = () => { };
      const always = () => true;
      const never = () => false;
      // flow content through FlowBoxes.
      // the caller is responsible for managing
      // and creating regions.
      const flowIntoRegions = async (opts) => {
          var _a, _b, _c, _d, _e, _f;
          const content = opts.content;
          const createRegion = opts.createRegion;
          if (!content)
              throw Error('content not specified');
          if (!createRegion)
              throw Error('createRegion not specified');
          // optional
          const applySplit = (_a = opts.applySplit) !== null && _a !== void 0 ? _a : noop;
          const canSplit = (_b = opts.canSplit) !== null && _b !== void 0 ? _b : always;
          const beforeAdd = (_c = opts.beforeAdd) !== null && _c !== void 0 ? _c : noop;
          const afterAdd = (_d = opts.afterAdd) !== null && _d !== void 0 ? _d : noop;
          const didWaitFor = (_e = opts.didWaitFor) !== null && _e !== void 0 ? _e : noop;
          const shouldTraverse = (_f = opts.shouldTraverse) !== null && _f !== void 0 ? _f : never;
          // currentRegion should hold the only state that persists during traversal.
          let currentRegion = createRegion();
          const hasOverflowed = () => currentRegion.hasOverflowed();
          const canSplitCurrent = () => canSplit(currentRegion.currentElement);
          const ignoreCurrentOverflow = () => ignoreOverflow(currentRegion.currentElement);
          const splitRules = (original, clone, nextChild, deepClone) => {
              if (original.tagName === 'OL') {
                  preserveNumbering(original, clone, nextChild);
              }
              if (original.tagName === 'TR' && nextChild && deepClone) {
                  preserveTableColumns(original, clone, nextChild, deepClone);
              }
              applySplit(original, clone, nextChild, deepClone);
          };
          const continueInNextRegion = () => {
              const prevRegion = currentRegion;
              currentRegion = createRegion();
              const newPath = clonePath(prevRegion.path, splitRules);
              currentRegion.setPath(newPath);
              return currentRegion;
          };
          const continuedParent = () => {
              continueInNextRegion();
              return currentRegion.currentElement;
          };
          const addText = async (textNode, isSplittable) => {
              const el = currentRegion.currentElement;
              let textLayout;
              if (isSplittable) {
                  // Add the text word by word, adding pages as needed
                  textLayout = await fillWords(textNode, el, continuedParent, hasOverflowed);
                  if (!textLayout.completed && currentRegion.path.length > 1) {
                      tryInNextRegion(currentRegion, continueInNextRegion, canSplit);
                      textLayout = await fillWords(textNode, el, continuedParent, hasOverflowed);
                  }
              }
              else {
                  // Add the text as a block, trying a new page if needed
                  textLayout = await addInOneGo(textNode, currentRegion.currentElement, hasOverflowed);
                  if (!textLayout.completed && !ignoreCurrentOverflow()) {
                      tryInNextRegion(currentRegion, continueInNextRegion, canSplit);
                      textLayout = await addInOneGo(textNode, currentRegion.currentElement, hasOverflowed);
                  }
              }
              // Something went wrong. Insert the text anyways, ignoring overflow,
              // and move onto the next region.
              if (!textLayout.completed) {
                  currentRegion.currentElement.appendChild(textNode);
                  if (!ignoreCurrentOverflow() && canSplitCurrent()) {
                      currentRegion.suppressErrors = true;
                      continueInNextRegion();
                  }
              }
          };
          const shouldTraverseChildren = (element) => {
              if (hasOverflowed())
                  return true;
              if (element.querySelector('img'))
                  return true;
              if (shouldTraverse(element))
                  return true;
              return false;
          };
          const addElement = async (element) => {
              // Ensure images are loaded before testing for overflow
              if (isUnloadedImage(element)) {
                  const waitTime = await ensureImageLoaded(element);
                  didWaitFor(waitTime);
              }
              // Transforms before adding
              await beforeAdd(element, continueInNextRegion);
              // Append element and push onto the the stack
              currentRegion.currentElement.appendChild(element);
              currentRegion.path.push(element);
              if (shouldTraverseChildren(element)) {
                  // Only if the region overflowed, the content contains
                  // an image, or the caller has requested a custom traversal.
                  await clearAndAddChildren(element);
              }
              // We're done: Pop off the stack and do any cleanup
              const addedElement = currentRegion.path.pop();
              await afterAdd(addedElement, continueInNextRegion);
          };
          const clearAndAddChildren = async (element) => {
              const childNodes = [...element.childNodes];
              element.innerHTML = '';
              if (hasOverflowed() && !ignoreCurrentOverflow() && canSplitCurrent()) {
                  // Overflows when empty
                  tryInNextRegion(currentRegion, continueInNextRegion, canSplit);
              }
              const shouldSplit = canSplit(element) && !ignoreOverflow(element);
              for (const child of childNodes) {
                  if (isTextNode(child)) {
                      await addText(child, shouldSplit);
                  }
                  else if (isContentElement(child)) {
                      await addElement(child);
                  }
              }
          };
          return addElement(content);
      };

      class HierarchyToHeadingAdapter {
          constructor(getter) {
              // console.warn('Deprecated');
              this.getHierarchy = getter;
          }
          textFor(sel) {
              var _a, _b;
              return (_b = (_a = this.getHierarchy()) === null || _a === void 0 ? void 0 : _a.find(entry => (entry === null || entry === void 0 ? void 0 : entry.selector) === sel)) === null || _b === void 0 ? void 0 : _b.text;
          }
          get h1() {
              return this.textFor('h1');
          }
          get h2() {
              return this.textFor('h2');
          }
          get h3() {
              return this.textFor('h3');
          }
          get h4() {
              return this.textFor('h4');
          }
          get h5() {
              return this.textFor('h5');
          }
          get h6() {
              return this.textFor('h6');
          }
      }
      class Page {
          constructor() {
              this.hierarchy = [];
              this.suppress = false;
              this.hasOutOfFlowContent = false;
              this.alwaysLeft = false;
              this.alwaysRight = false;
              this.isOutOfFlow = false; // used by spreads
              this.avoidReorder = false; // used by 2-page spreads
              this.flow = new Region(div('flow-box'));
              this.footer = div('footer');
              this.background = div('page-background');
              this.element = div('page', this.background, this.flow.element, this.footer);
              this.heading = new HierarchyToHeadingAdapter(() => this.hierarchy);
          }
          static isSizeValid() {
              const testPage = new Page();
              return safeMeasure(testPage.element, () => testPage.flow.isReasonableSize);
          }
          setLeftRight(dir) {
              this.side = dir;
              this.element.classList.toggle(classes.leftPage, this.isLeft);
              this.element.classList.toggle(classes.rightPage, !this.isLeft);
          }
          get isLeft() {
              return this.side === 'left';
          }
          get isRight() {
              return this.side === 'right';
          }
          // TODO(milahu) implement
          /* non trivial
          get isLast() {
              return this.side === 'right';
          }
          */
          setPreference(dir) {
              const preferLeft = dir === 'left';
              this.alwaysLeft = preferLeft;
              this.alwaysRight = !preferLeft;
          }
          get suppressErrors() {
              var _a;
              return (_a = this.suppress) !== null && _a !== void 0 ? _a : false;
          }
          set suppressErrors(newVal) {
              this.suppress = newVal;
              this.element.classList.toggle(classes.isOverflowing, newVal);
          }
          get isEmpty() {
              return !this.hasOutOfFlowContent && this.flow.isEmpty();
          }
          validate() {
              if (!this.hasOverflowed())
                  return;
              const suspect = this.flow.currentElement;
              if (suspect) {
                  console.warn('Bindery: Content overflows, probably due to a style set on:', suspect);
                  if (suspect.parentNode) {
                      suspect.parentNode.removeChild(suspect);
                  }
              }
              else {
                  console.warn('Bindery: Content overflows.');
              }
          }
          validateEnd(allowOverflow) {
              if (!this.hasOverflowed())
                  return;
              console.warn(`Bindery: Page ~${this.number} is overflowing`, this.element);
              if (!this.suppressErrors && !this.flow.suppressErrors && !allowOverflow) {
                  throw Error('Bindery: Moved to new page when last one is still overflowing');
              }
          }
          hasOverflowed() {
              return safeMeasure(this.element, () => this.flow.hasOverflowed());
          }
      }

      const indexOfNextReorderablePage = (pages, startIndex) => {
          for (let i = startIndex; i < pages.length; i += 1) {
              const pg = pages[i];
              if (!pg.isOutOfFlow && !pg.avoidReorder)
                  return i;
          }
          return null;
      };
      // Given an array of pages with alwaysLeft, alwaysRight, and isOutOfFlow
      // properties, orders them so that alwaysLeft and alwaysRight are true.
      const orderPages = (pages, makeNewPage) => {
          const orderedPages = pages.slice();
          for (let i = 0; i < orderedPages.length; i += 1) {
              const page = orderedPages[i];
              const isLeft = i % 2 !== 0;
              if ((isLeft && page.alwaysRight) || (!isLeft && page.alwaysLeft)) {
                  if (page.isOutOfFlow) {
                      // If the page is 'out of flow', we'd prefer not to add a blank page.
                      // Instead it floats backwards in the book, pulling the next
                      // in-flow page forward. If several 'out of flow' pages
                      // are next to each other, they will remain in order, all being pushed
                      // backward together.
                      const indexToSwap = indexOfNextReorderablePage(orderedPages, i + 1);
                      if (!indexToSwap) {
                          // No larger index to swap with, perhaps because
                          // we are optimistically rendering before the book is done
                          break;
                      }
                      const pageToMoveUp = orderedPages[indexToSwap];
                      orderedPages.splice(indexToSwap, 1); // remove pg
                      orderedPages.splice(i, 0, pageToMoveUp); // insert pg
                  }
                  else {
                      // If the page is 'in flow', order must be respected, so extra blank pages
                      // are inserted.
                      orderedPages.splice(i, 0, makeNewPage());
                  }
              }
          }
          return orderedPages;
      };

      const MAXIMUM_PAGE_LIMIT = 2000;
      class Book {
          constructor() {
              this.rawPages = [];
              this.orderedPages = [];
          }
          addPage(newPage) {
              this.rawPages.push(newPage);
              this.updatePageOrder();
          }
          get pageCount() {
              return this.orderedPages.length;
          }
          get pages() {
              return this.orderedPages;
          }
          updatePageOrder() {
              this.orderedPages = orderPages(this.rawPages, () => new Page());
          }
          validate() {
              if (this.pageCount > MAXIMUM_PAGE_LIMIT) {
                  throw Error('Bindery: Maximum page count exceeded. Suspected runaway layout.');
              }
          }
      }

      const annotatePagesNumbers = (pages, offset) => {
          {
              pages.forEach((page, i) => {
                  page.number = offset + i + 1;
                  page.setLeftRight(i % 2 === 0 ? 'right' : 'left');
              });
          }
      };
      const annotatePagesHierarchy = (pages, headerSelectorHierarchy) => {
          // ———
          // RUNNING HEADERS
          // Sections to annotate with.
          // This should be a hierarchical list of selectors.
          // Every time one is selected, it annotates all following pages
          // and clears any subselectors.
          let currentHierarchy = [];
          pages.forEach(page => {
              const pageHierarchy = [];
              headerSelectorHierarchy.forEach((selector, i) => {
                  var _a;
                  const element = page.element.querySelector(selector);
                  // A new header level starts on this page
                  if (element) {
                      currentHierarchy[i] = {
                          selector: selector,
                          text: (_a = element.textContent) !== null && _a !== void 0 ? _a : '',
                          el: element,
                      };
                      // Clear any lower headers in the hierarchy
                      currentHierarchy = currentHierarchy.slice(0, i + 1);
                      // headerSelectorHierarchy.forEach((lowerSelector, j) => {
                      //   if (j > i) {
                      //     currentHeaders[j] = { selector: lowerSelector, text: '', el: undefined };
                      //   }
                      // });
                  }
                  // Always decorate this page with current header state.
                  if (currentHierarchy[i]) {
                      pageHierarchy[i] = currentHierarchy[i];
                  }
              });
              page.hierarchy = pageHierarchy;
          });
      };
      const annotatePages = (pages, offset) => {
          annotatePagesNumbers(pages, offset);
          annotatePagesHierarchy(pages, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
      };

      const pageNumbersForTest = (pages, test) => {
          return pages
              .filter(pg => !!pg.number)
              .filter(pg => test(pg.element))
              .map(pg => pg.number);
      };

      // Compatible with ids that start with numbers
      const startsWithNumber = (sel) => {
          return sel.length > 2 && sel[0] === '#' && /^\d+$/.test(sel[1]);
      };
      const safeIDSel = (sel) => {
          return startsWithNumber(sel) ? `[id="${sel.replace('#', '')}"]` : sel;
      };
      class PageReference extends Replace {
          constructor(options) {
              super(options);
              validateRuntimeOptions(options, {
                  name: 'PageReference',
                  selector: RuntimeTypes.string,
                  replace: RuntimeTypes.func,
                  createTest: RuntimeTypes.func,
              });
              this.references = [];
              const throttle = throttleTime(10);
              this.throttledUpdate = book => {
                  throttle(() => this.updatePageReferences(book.pages));
              };
          }
          eachPage(page, book) {
              this.throttledUpdate(book);
          }
          afterAdd(elmt, book) {
              const test = this.createTest(elmt);
              if (!test)
                  return elmt;
              const ref = this.createReference(book, test, elmt);
              return ref.element;
          }
          createReference(book, testFunction, elmt) {
              const ref = {
                  testFunction,
                  template: elmt,
                  element: elmt,
                  previousMatches: undefined,
              };
              this.references.push(ref);
              const currentResults = pageNumbersForTest(book.pages, testFunction);
              this.render(ref, currentResults); // Replace element immediately, to make sure it'll fit
              return ref;
          }
          render(ref, matchingPageNumbers) {
              if (ref.previousMatches && shallowEqual(ref.previousMatches, matchingPageNumbers)) {
                  return;
              }
              if (!Array.isArray(matchingPageNumbers)) {
                  throw Error('Page search returned unexpected result');
              }
              const hasFoundPage = matchingPageNumbers.length > 0;
              const pageRanges = hasFoundPage ? formatAsRanges(matchingPageNumbers) : '⌧';
              const template = ref.template.cloneNode(true);
              const newRender = this.replace(template, pageRanges);
              if (!hasFoundPage)
                  newRender.classList.add(prefixer('placeholder-num'));
              ref.element.parentNode.replaceChild(newRender, ref.element);
              ref.element = newRender;
              ref.previousMatches = matchingPageNumbers;
          }
          createTest(element) {
              const href = element.getAttribute('href');
              if (!href)
                  return null;
              const selector = safeIDSel(href);
              return (el) => {
                  return !!el.querySelector(selector);
              };
          }
          updatePageReferences(pages) {
              // querySelector first, then rerender
              const results = this.references.map(ref => {
                  return { ref, matchingPageNumbers: pageNumbersForTest(pages, ref.testFunction) };
              });
              results.forEach(({ ref, matchingPageNumbers }) => this.render(ref, matchingPageNumbers));
          }
          replace(template, number) {
              template.insertAdjacentHTML('beforeend', `, <span>${number}</span>`);
              return template;
          }
      }

      class PageBreak extends Rule {
          constructor(options) {
              var _a, _b;
              super(options);
              if (options.continue == 'same') {
                  throw Error("Can't continue on the same pager after a Page Break ");
              }
              this.continue = (_a = options.continue) !== null && _a !== void 0 ? _a : 'next';
              this.position = (_b = options.position) !== null && _b !== void 0 ? _b : 'before';
              validateRuntimeOptions(options, {
                  name: 'PageBreak',
                  selector: RuntimeTypes.string,
                  continue: RuntimeTypes.enum('next', 'left', 'right'),
                  position: RuntimeTypes.enum('before', 'after', 'both', 'avoid'),
              });
          }
          get avoidSplit() {
              return this.position === 'avoid';
          }
          beforeAdd(elmt, book, continueOnNewPage) {
              if (this.position === 'before' || this.position === 'both') {
                  if (!book.currentPage.isEmpty) {
                      continueOnNewPage();
                  }
                  if (this.continue !== 'next') {
                      book.currentPage.setPreference(this.continue);
                  }
              }
              return elmt;
          }
          afterAdd(elmt, book, continueOnNewPage) {
              if (this.position === 'after' || this.position === 'both') {
                  continueOnNewPage();
                  if (this.continue !== 'next') {
                      book.currentPage.setPreference(this.continue);
                  }
              }
              return elmt;
          }
      }

      // TODO selectorHierarchy: [ String ], ie [ 'h1', 'h2', 'h3.chapter' ]
      class RunningHeader extends Rule {
          constructor(options = {}) {
              super(options);
              validateRuntimeOptions(options, {
                  name: 'RunningHeader',
                  render: RuntimeTypes.func,
              });
          }
          eachPage(page) {
              if (!page.runningHeader) {
                  const elmt = div('.running-header');
                  page.element.appendChild(elmt);
                  page.runningHeader = elmt;
              }
              page.runningHeader.innerHTML = this.render(page);
          }
          render(page) {
              return `${page.number}`;
          }
      }

      class Split extends Rule {
          constructor(options) {
              super(options);
              this.toNext = options.toNext;
              this.fromPrevious = options.fromPrevious;
              validateRuntimeOptions(options, {
                  name: 'Split',
                  selector: RuntimeTypes.string,
                  toNext: RuntimeTypes.string,
                  fromPrevious: RuntimeTypes.string,
                  didSplit: RuntimeTypes.func,
              });
          }
          didSplit(original, clone) {
              if (this.toNext)
                  original.classList.add(this.toNext);
              if (this.fromPrevious)
                  clone.classList.add(this.fromPrevious);
          }
      }

      var rules = {
          Rule,
          Split(options) {
              return new Split(options);
          },
          Counter(options) {
              return new Counter(options);
          },
          FullBleedPage(options) {
              return new FullBleedPage(options);
          },
          Footnote(options) {
              return new Footnote(options);
          },
          RunningHeader(options) {
              return new RunningHeader(options);
          },
          Replace(options) {
              return new Replace(options);
          },
          FullBleedSpread(options) {
              return new FullBleedSpread(options);
          },
          PageBreak(options) {
              return new PageBreak(options);
          },
          PageReference(options) {
              return new PageReference(options);
          },
          createRule(options) {
              return new Rule(options);
          },
      };

      const { PageBreak: PageBreak$1, PageReference: PageReference$1, Footnote: Footnote$1, FullBleedPage: FullBleedPage$1, FullBleedSpread: FullBleedSpread$1, } = rules;
      const replacer = (element, number) => {
          element.textContent = `${number}`;
          return element;
      };
      var attributeRules = [
          PageBreak$1({ selector: '[book-page-break="both"]', position: 'both' }),
          PageBreak$1({ selector: '[book-page-break="avoid"]', position: 'avoid' }),
          PageBreak$1({
              selector: '[book-page-break="after"][book-page-continue="right"]',
              position: 'after',
              continue: 'right',
          }),
          PageBreak$1({
              selector: '[book-page-break="after"][book-page-continue="left"]',
              position: 'after',
              continue: 'left',
          }),
          PageBreak$1({
              selector: '[book-page-break="after"][book-page-continue="next"]',
              position: 'after',
              continue: 'next',
          }),
          PageBreak$1({
              selector: '[book-page-break="before"][book-page-continue="right"]',
              position: 'before',
              continue: 'right',
          }),
          PageBreak$1({
              selector: '[book-page-break="before"][book-page-continue="left"]',
              position: 'before',
              continue: 'left',
          }),
          PageBreak$1({
              selector: '[book-page-break="before"][book-page-continue="next"]',
              position: 'before',
              continue: 'next',
          }),
          FullBleedPage$1({ selector: '[book-full-bleed="page"]' }),
          FullBleedSpread$1({ selector: '[book-full-bleed="spread"]' }),
          Footnote$1({
              selector: '[book-footnote-text]',
              render: (element, number) => {
                  const txt = element.getAttribute('book-footnote-text');
                  return `<i>${number}</i>${txt}`;
              },
          }),
          PageReference$1({
              selector: '[book-pages-with-text]',
              replace: replacer,
              createTest: (element) => {
                  var _a;
                  const text = (_a = element.getAttribute('book-pages-with-text')) !== null && _a !== void 0 ? _a : '';
                  const term = text.toLowerCase().trim();
                  return (pageElement) => {
                      const pageText = pageElement.textContent || '';
                      return pageText.toLowerCase().includes(term);
                  };
              },
          }),
          PageReference$1({
              selector: '[book-pages-with-selector]',
              replace: replacer,
              createTest: (element) => {
                  var _a;
                  const txt = (_a = element.getAttribute('book-pages-with-selector')) !== null && _a !== void 0 ? _a : '';
                  const selector = txt.trim();
                  return (pageElement) => {
                      return !!pageElement.querySelector(selector);
                  };
              },
          }),
          PageReference$1({
              selector: '[book-pages-with]',
              replace: replacer,
              createTest: (element) => {
                  var _a;
                  const text = (_a = element.textContent) !== null && _a !== void 0 ? _a : '';
                  const term = text.toLowerCase().trim();
                  return (pageElement) => {
                      var _a;
                      const pageText = (_a = pageElement.textContent) !== null && _a !== void 0 ? _a : '';
                      return pageText.toLowerCase().includes(term);
                  };
              },
          }),
      ];

      const isSpread = (rule) => rule instanceof FullBleedSpread;
      const isPage = (rule) => rule instanceof FullBleedPage;
      const isBreak = (rule) => rule instanceof PageBreak;
      const isFullPageRule = (rule) => isSpread(rule) || isPage(rule) || isBreak(rule);
      const dedupe = (inputRules) => {
          const conflictRules = inputRules.filter(isFullPageRule);
          const output = inputRules.filter(rule => !conflictRules.includes(rule));
          const firstSpreadRule = conflictRules.find(isSpread);
          const firstPageRule = conflictRules.find(isPage);
          // Only apply one fullpage or fullspread
          if (firstSpreadRule)
              output.push(firstSpreadRule);
          else if (firstPageRule)
              output.push(firstPageRule);
          else
              output.push(...conflictRules); // but multiple pagebreaks are ok
          return output;
      };

      // TODO:
      // While this does catch overflows, it is pretty hacky to move the entire node to the next page.
      // - 1. there is no guarentee it will fit on the new page
      // - 2. if it had childNodes, those side effects will not be undone,
      // which means footnotes will get left on previous page.
      // - 3. if it is a large paragraph, it will leave a large gap. the
      // ideal approach would be to only need to invalidate the last line of text.
      const recoverFromRule = (el, book, nextRegion) => {
          let removed = el;
          const parent = el.parentNode;
          if (!parent) {
              throw Error("Can't recoverFromRule when element is unparented");
          }
          parent.removeChild(removed);
          let popped;
          if (book.currentPage.hasOverflowed()) {
              parent.appendChild(el);
              removed = parent;
              if (!removed.parentNode) {
                  throw Error("Can't recoverFromRule when element is unparented");
              }
              removed.parentNode.removeChild(removed);
              popped = book.currentPage.flow.path.pop();
              if (book.currentPage.hasOverflowed()) {
                  console.error('Trying again didnt fix it');
              }
          }
          const newRegion = nextRegion();
          newRegion.currentElement.appendChild(removed);
          if (popped)
              newRegion.path.push(popped);
      };

      const giveUp = (rule, el) => {
          console.warn(`Couldn't apply ${rule.name}, caused overflows twice when adding: `, el);
      };
      function isPageRule(rule) {
          return rule.eachPage;
      }
      function isBeforeAddRule(rule) {
          return !!rule.selector && rule.beforeAdd;
      }
      function isAfterAddRule(rule) {
          return !!rule.selector && rule.afterAdd;
      }
      function isOffsetRule(rule) {
          return rule.pageNumberOffset;
      }
      function isDidSplitRule(rule) {
          return !!rule.selector && rule.didSplit;
      }
      function isAvoidSplitRule(rule) {
          return !!rule.selector && rule.avoidSplit;
      }
      class RuleSet {
          constructor(rules) {
              var _a;
              const offsetRule = rules.find(isOffsetRule);
              this.pageNumberOffset = (_a = offsetRule === null || offsetRule === void 0 ? void 0 : offsetRule.pageNumberOffset) !== null && _a !== void 0 ? _a : 0;
              // Rules for pages
              this.pageRules = rules.filter(isPageRule);
              // Rules for elements
              this.beforeAddRules = rules.filter(isBeforeAddRule);
              this.afterAddRules = rules.filter(isAfterAddRule);
              // Rules for layout
              this.selectorsNotToSplit = rules
                  .filter(isAvoidSplitRule)
                  .map(r => r.selector);
              this.didSplitRules = rules.filter(isDidSplitRule);
              // setup
              rules.filter(r => r.setup).forEach(r => r.setup());
              this.applySplitRules = this.applySplitRules.bind(this);
              const allSelectors = rules
                  .map(r => r.selector)
                  .filter(sel => !!sel)
                  .join(', ');
              if (allSelectors) {
                  const shouldTraverse = (el) => !!el.querySelector(allSelectors);
                  this.shouldTraverse = shouldTraverse.bind(this);
              }
              else {
                  this.shouldTraverse = () => false;
              }
          }
          applySplitRules(original, clone) {
              original.classList.add(classes.toNext);
              clone.classList.add(classes.fromPrev);
              this.didSplitRules
                  .filter(r => original.matches(r.selector))
                  .forEach(rule => {
                  rule.didSplit(original, clone);
              });
          }
          // Rules for pages
          applyPageDoneRules(pg, book) {
              this.pageRules.forEach(rule => rule.eachPage(pg, book));
          }
          finishEveryPage(book) {
              this.pageRules.forEach(rule => book.pages.forEach(pg => rule.eachPage(pg, book)));
          }
          // Rules for elements
          applyBeforeAddRules(element, book, continueOnNewPage, makeNewPage) {
              let addedElement = element;
              const matchingRules = this.beforeAddRules.filter(rule => addedElement.matches(rule.selector));
              matchingRules.forEach(rule => {
                  addedElement = rule.beforeAdd(addedElement, book, continueOnNewPage, makeNewPage);
              });
              return addedElement;
          }
          applyAfterAddRules(originalElement, book, continueOnNewPage, makeNewPage) {
              let addedElement = originalElement;
              const attemptRecovery = (el) => recoverFromRule(el, book, continueOnNewPage);
              const matchingRules = this.afterAddRules.filter(rule => addedElement.matches(rule.selector));
              const uniqueRules = dedupe(matchingRules);
              uniqueRules.forEach(rule => {
                  const retry = (el) => {
                      attemptRecovery(el);
                      return rule.afterAdd(el, book, continueOnNewPage, makeNewPage, () => giveUp(rule, el));
                  };
                  addedElement = rule.afterAdd(addedElement, book, continueOnNewPage, makeNewPage, retry);
              });
              return addedElement;
          }
      }

      const sec = (ms) => (ms / 1000).toFixed(2);
      const estimateFor = (content) => {
          const start = window.performance.now();
          const capacity = content.querySelectorAll('*').length;
          let timeWaiting = 0;
          let completed = 0;
          return {
              increment: () => {
                  completed += 1;
              },
              addWaitTime: (t) => {
                  timeWaiting += t;
              },
              get progress() {
                  return completed / capacity;
              },
              end: () => {
                  const end = window.performance.now();
                  const total = end - start;
                  const layout = total - timeWaiting;
                  console.log(`bindery Layout ready in ${sec(layout)}s (plus ${sec(timeWaiting)}s waiting for images)`);
              },
          };
      };

      const makeBook = async (content, rules, updateProgress) => {
          if (!Page.isSizeValid())
              throw Error('Page is too small');
          const estimator = estimateFor(content);
          const ruleSet = new RuleSet(rules);
          const book = new Book();
          const pageNumberOffset = ruleSet.pageNumberOffset;
          const makeNewPage = () => new Page();
          const finishPage = (page, allowOverflow) => {
              // finished with this page, can display
              book.updatePageOrder();
              annotatePages(book.pages, pageNumberOffset);
              ruleSet.applyPageDoneRules(page, book);
              page.validateEnd(allowOverflow);
              book.validate();
          };
          const addPageToBook = (allowOverflow = false) => {
              const oldPage = book.currentPage;
              if (oldPage)
                  finishPage(oldPage, allowOverflow);
              const newPage = makeNewPage();
              book.currentPage = newPage;
              book.addPage(newPage);
              updateProgress(book, estimator.progress);
              newPage.validate();
              return newPage;
          };
          const makeNextRegion = () => {
              const newPage = addPageToBook();
              return newPage.flow;
          };
          const applySplit = ruleSet.applySplitRules;
          const dontSplitSel = ruleSet.selectorsNotToSplit;
          const canSplit = (element) => {
              if (dontSplitSel.some(sel => element.matches(sel))) {
                  return false;
              }
              if (element.parentElement)
                  return canSplit(element.parentElement);
              return true;
          };
          const beforeAdd = async (elementToAdd, continueInNextRegion) => {
              ruleSet.applyBeforeAddRules(elementToAdd, book, continueInNextRegion, makeNewPage);
          };
          const afterAdd = async (addedElement, continueInNextRegion) => {
              estimator.increment();
              return ruleSet.applyAfterAddRules(addedElement, book, continueInNextRegion, makeNewPage);
          };
          // init
          content.style.margin = '0';
          content.style.padding = '0';
          await flowIntoRegions({
              content,
              createRegion: makeNextRegion,
              applySplit,
              canSplit,
              beforeAdd,
              afterAdd,
              shouldTraverse: ruleSet.shouldTraverse,
              didWaitFor: t => estimator.addWaitTime(t),
          });
          book.updatePageOrder();
          annotatePages(book.pages, pageNumberOffset);
          ruleSet.finishEveryPage(book);
          estimator.end();
          return book;
      };

      const fetchContent = async (url, selector) => {
          const response = await fetch(url);
          if (response.status !== 200) {
              throw Error(`Response ${response.status}: Could not load file at "${url}"`);
          }
          const fetchedContent = await response.text();
          const el = parseHTML(fetchedContent, selector);
          if (!(el instanceof HTMLElement)) {
              throw Error(`Could not find element that matches selector "${selector}" in response from ${url}`);
          }
          return el;
      };
      const getContentAsElement = async (content) => {
          if (content instanceof HTMLElement)
              return content;
          if (typeof content === 'string') {
              const el = document.querySelector(content);
              if (!(el instanceof HTMLElement)) {
                  throw Error(`Could not find element that matches selector "${content}"`);
              }
              return el;
          }
          if (typeof content === 'object' && content.url) {
              return fetchContent(content.url, content.selector);
          }
          throw Error('Content source must be an element or selector');
      };

      // TODO: This is not a particularly robust check.
      const supportsCustomSheetSize = () => !!window.hasOwnProperty('chrome');
      const getSheetSizeLabels = (pageSize) => {
          const sizeName = `${pageSize.width} × ${pageSize.height}`;
          if (!supportsCustomSheetSize()) {
              return [
                  [SheetSize.LETTER_PORTRAIT, 'Default Page Size *'],
                  [
                      SheetSize.LETTER_PORTRAIT,
                      "Only Chrome supports custom page sizes. Set in your browser's print dialog instead.",
                  ],
              ];
          }
          return [
              [SheetSize.AUTO, `${sizeName}`],
              [SheetSize.AUTO_BLEED, `${sizeName} + Bleed`],
              [SheetSize.AUTO_MARKS, `${sizeName} + Marks`],
              [SheetSize.LETTER_PORTRAIT, 'Letter Portrait'],
              [SheetSize.LETTER_LANDSCAPE, 'Letter Landscape'],
              [SheetSize.A4_PORTRAIT, 'A4 Portrait'],
              [SheetSize.A4_LANDSCAPE, 'A4 Landscape'],
              [SheetSize.A5_PORTRAIT, 'A5 Portrait'],
              [SheetSize.A5_LANDSCAPE, 'A5 Landscape'],
          ];
      };
      const marksLabels = [
          [SheetMarks.NONE, 'No Marks'],
          [SheetMarks.CROP, 'Crop Marks'],
          [SheetMarks.BLEED, 'Bleed Marks'],
          [SheetMarks.BOTH, 'Crop and Bleed'],
      ];
      const modeLabels = [
          [ViewerMode.LINEAR, 'Linear'],
          [ViewerMode.PREVIEW, 'Grid'],
          [ViewerMode.FLIPBOOK, 'Flipbook'],
          [ViewerMode.PRINT, 'Print Preview'],
      ];
      const layoutLabels = [
          [SheetLayout.PAGES, '1 Page / Sheet'],
          [SheetLayout.SPREADS, '1 Spread / Sheet'],
          [SheetLayout.BOOKLET, 'Booklet Sheets'],
      ];

      const row = (cls, ...children) => {
          return div(`${cls}.row`, ...children);
      };
      // Button
      const btn = (cls, attrs, label) => {
          return button(`.control.btn${cls}`, attrs, label);
      };
      const dropdown = (attrs, options) => {
          const selectVal = div('.select-val', 'Value');
          const selectEl = select('.select', attrs, ...options);
          selectVal.textContent = selectEl.options[selectEl.selectedIndex].text;
          return div('.select-wrap.control', selectVal, selectEl);
      };
      const enumDropdown = (id, entries, initialValue, changeHandler) => {
          const eventHandler = (e) => {
              const rawVal = e.target.value;
              const chosenEntry = entries.filter(entry => entry[0].toString() === rawVal)[0];
              if (chosenEntry) {
                  changeHandler(chosenEntry[0]);
              }
              else {
                  throw Error('Selected unknown value');
              }
          };
          return dropdown({ onchange: eventHandler, id }, entries.map(entry => {
              const el = option({ value: entry[0] }, entry[1]);
              if (entry[0] === initialValue) {
                  el.selected = true;
              }
              return el;
          }));
      };

      const renderPrintOptions = (state, actions) => {
          const shouldShowMarks = state.paper !== SheetSize.AUTO && state.paper !== SheetSize.AUTO_BLEED;
          const sizeLabels = getSheetSizeLabels(state.pageSize);
          return (
            row(
              '.print-options',
              row(null, enumDropdown('bindery-choose-layout', layoutLabels, state.layout, actions.setLayout)),
              row(null, enumDropdown('bindery-choose-paper', sizeLabels, state.paper, actions.setPaper)),
              shouldShowMarks
                ? row(null, enumDropdown('bindery-choose-marks', marksLabels, state.marks, actions.setMarks))
                : ''
            )
          );
      };
      function binderyExtraButtons(state, actions) {
        return [
          //h('a', '.btn-print.btn-main', { href: 'https://github.com/milahu/alchi' }, 'Github'),
          h('a', '.btn.control', { href: 'https://github.com/milahu/alchi' }, 'Github'),
        ];
      }
      class Controls {
          constructor() {
              this.element = div('.controls');
          }
          update(state, actions) {
              var _a, _b;
              const oldElement = this.element;
              const newElement = this.render(state, actions);
              const focusedId = document.hasFocus() ? (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.id : undefined;
              oldElement.replaceWith(newElement);
              if (focusedId)
                  (_b = document.getElementById(focusedId)) === null || _b === void 0 ? void 0 : _b.focus();
              this.element = newElement;
          }
          render(state, actions) {
              const print = () => {
                  actions.setMode(ViewerMode.PRINT);
                  setTimeout(window.print, 10);
              };
              const shouldShowPrint = state.mode === ViewerMode.PRINT;
              return (
                div(
                  '.controls',
                  row('.view-row', enumDropdown('bindery-choose-view', modeLabels, state.mode, actions.setMode)),
                  shouldShowPrint ? renderPrintOptions(state, actions) : '',
                  ...((binderyExtraButtons && binderyExtraButtons(state, actions)) || []),
                  //btn('.btn-print.btn-main', { onclick: print }, 'Print')
                  btn('.btn', { onclick: print }, 'Print')
                )
              );
          }
      }

      const padPages = (pages, makePage) => {
          if (pages.length % 2 !== 0) {
              const pg = makePage();
              pages.push(pg);
          }
          const spacerPage = makePage();
          const spacerPage2 = makePage();
          spacerPage.element.style.visibility = 'hidden';
          spacerPage2.element.style.visibility = 'hidden';
          pages.unshift(spacerPage);
          pages.push(spacerPage2);
          return pages;
      };

      const twoPageSpread = (...children) => {
          return div('.spread-wrapper.spread-centered.spread-size', ...children);
      };
      const onePageSpread = (...children) => {
          return div('.spread-wrapper.spread-centered.page-size', ...children);
      };
      function renderLinearViewer(bookPages) {
        const pages = bookPages;
          const linearLayout = document.createDocumentFragment();
          pages.forEach(pg => {
              const wrap = onePageSpread(pg.element);
              linearLayout.appendChild(wrap);
          });
          return {
              element: linearLayout,
          };
      }
      const renderGridViewer = (bookPages, isTwoUp) => {
          const pages = isTwoUp ? padPages(bookPages, () => new Page()) : bookPages;
          const gridLayout = document.createDocumentFragment();
          if (isTwoUp) {
              for (let i = 0; i < pages.length; i += 2) {
                  const wrap = twoPageSpread(pages[i].element, pages[i + 1].element);
                  gridLayout.appendChild(wrap);
              }
          }
          else {
              pages.forEach(pg => {
                  const wrap = onePageSpread(pg.element);
                  gridLayout.appendChild(wrap);
              });
          }
          return {
              element: gridLayout,
          };
      };

      const directions = ['top', 'bottom', 'left', 'right'];
      const bleedMarks = () => directions.map(dir => div(`.mark-bleed-${dir}`));
      const cropMarks = () => directions.map(dir => div(`.mark-crop-${dir}`));
      const pageSheetMarks = () => div('.page-size.print-mark-wrap', ...cropMarks(), ...bleedMarks());
      const spreadSheetMarks = () => div('.spread-size.print-mark-wrap', div('.mark-crop-fold'), ...cropMarks(), ...bleedMarks());
      const bookletMeta = (i, len) => {
          const isFront = i % 4 === 0;
          const sheetIndex = Math.round((i + 1) / 4) + 1;
          return div('.print-meta', `Sheet ${sheetIndex} of ${len / 4}: ${isFront ? 'Outside' : 'Inside'}`);
      };

      const orderPagesBooklet = (pages, makePage) => {
          while (pages.length % 4 !== 0) {
              const spacerPage = makePage();
              spacerPage.element.style.visibility = 'hidden';
              pages.push(spacerPage);
          }
          const bookletOrder = [];
          const len = pages.length;
          for (let i = 0; i < len / 2; i += 2) {
              bookletOrder.push(pages[len - 1 - i]);
              bookletOrder.push(pages[i]);
              bookletOrder.push(pages[i + 1]);
              bookletOrder.push(pages[len - 2 - i]);
          }
          return bookletOrder;
      };

      const twoPageSpread$1 = (...children) => {
          return div('.spread-wrapper', ...children);
      };
      const onePageSpread$1 = (...children) => {
          return div('.spread-wrapper', ...children);
      };
      const renderSheetViewer = (bookPages, _doubleSided, layout) => {
          const isTwoUp = layout !== SheetLayout.PAGES;
          const isSpreads = layout === SheetLayout.SPREADS;
          const isBooklet = layout === SheetLayout.BOOKLET;
          let pages = bookPages;
          if (isSpreads)
              pages = padPages(pages, () => new Page());
          else if (isBooklet)
              pages = orderPagesBooklet(pages, () => new Page());
          const printLayout = document.createDocumentFragment();
          const marks = isTwoUp ? spreadSheetMarks : pageSheetMarks;
          const spread = isTwoUp ? twoPageSpread$1 : onePageSpread$1;
          const printSheet = (...children) => {
              return div('.print-sheet', spread(...children));
          };
          if (isTwoUp) {
              for (let i = 0; i < pages.length; i += 2) {
                  const spreadMarks = marks();
                  if (isBooklet) {
                      const meta = bookletMeta(i, pages.length);
                      spreadMarks.appendChild(meta);
                  }
                  const sheet = printSheet(div('.page-bleed-clip.page-bleed-clip-left', pages[i]), div('.page-bleed-clip.page-bleed-clip-right', pages[i + 1]), spreadMarks);
                  sheet.classList.add(classes.sheetSpread);
                  printLayout.appendChild(sheet);
              }
          }
          else {
              pages.forEach(p => {
                  const pg = p;
                  const sheet = printSheet(pg.element, marks());
                  sheet.classList.add(pg.isLeft ? classes.sheetLeft : classes.sheetRight);
                  printLayout.appendChild(sheet);
              });
          }
          return {
              element: printLayout,
          };
      };

      const renderFlipbookViewer = (bookPages, doubleSided) => {
          const pages = padPages(bookPages, () => new Page());
          const flipLayout = document.createDocumentFragment();
          const sizer = div('.flipbook-sizer');
          const flapHolder = div('.spread-size.flap-holder');
          sizer.append(flapHolder);
          flipLayout.append(sizer);
          const flaps = [];
          let currentLeaf = -1;
          let leftOffset = 4;
          if (pages.length * leftOffset > 60) {
              leftOffset = 60 / pages.length;
          }
          flapHolder.style.width = `${pages.length * leftOffset}px`;
          const setLeaf = (unclamped) => {
              let n = unclamped;
              if (n === currentLeaf)
                  n += 1;
              const newLeaf = Math.min(Math.max(0, n), flaps.length);
              let zScale = 4;
              if (flaps.length * zScale > 200)
                  zScale = 200 / flaps.length;
              flaps.forEach((flap, i, arr) => {
                  // + 0.5 so left and right are even
                  const z = (arr.length - Math.abs(i - newLeaf + 0.5)) * zScale;
                  flap.style.transform = `translate3d(${i < newLeaf ? 4 : 0}px,0,${z}px) rotateY(${i < newLeaf ? -180 : 0}deg)`;
              });
              currentLeaf = newLeaf;
          };
          let leafIndex = 0;
          for (let i = 1; i < pages.length - 1; i += doubleSided ? 2 : 1) {
              leafIndex += 1;
              const li = leafIndex;
              const flap = div('.page3d');
              flap.addEventListener('click', () => {
                  const newLeaf = li - 1;
                  setLeaf(newLeaf);
              });
              const rightPage = pages[i].element;
              let leftPage;
              rightPage.classList.add(prefixer('page3d-front'));
              flap.append(rightPage);
              if (doubleSided) {
                  flap.classList.add(prefixer('doubleSided'));
                  leftPage = pages[i + 1].element;
              }
              else {
                  leftPage = div('.page');
              }
              leftPage.classList.add(prefixer('page3d-back'));
              flap.append(leftPage);
              // TODO: Virtualize stack of pages.
              // Putting 1000s of elements onscreen,
              // espacially as 3d layers, locks up the browser.
              flap.style.left = `${i * leftOffset}px`;
              flaps.push(flap);
              flapHolder.append(flap);
          }
          setLeaf(0);
          return {
              element: flipLayout,
              contentSizer: sizer,
              next: () => setLeaf(currentLeaf + 1),
              previous: () => setLeaf(currentLeaf - 1),
          };
      };

      const getScrollAsPercent = () => {
          if (!document || !document.scrollingElement)
              return 0;
          const el = document.scrollingElement;
          return el.scrollTop / el.scrollHeight;
      };
      const scrollToPercent = (newVal) => {
          if (!document.scrollingElement)
              return;
          const el = document.scrollingElement;
          el.scrollTop = el.scrollHeight * newVal;
      };
      const scrollToBottom = () => {
          const scroll = document.scrollingElement;
          if (!scroll)
              return;
          const scrollMax = scroll.scrollHeight - scroll.offsetHeight;
          scroll.scrollTop = scrollMax;
      };

      /* global BINDERY_VERSION */
      var errorView = (title, text) => {
          return div('.error', div('.error-title', title), div('.error-text', text), div('.error-footer', `Bindery ${BINDERY_VERSION}`));
      };

      const isCommandP = (e) => {
          return (e.ctrlKey || e.metaKey) && e.keyCode === 80;
      };
      // Automatically switch into print mode
      const listenForPrint = (beforePrint) => {
          if (window.matchMedia) {
              const mediaQueryList = window.matchMedia('print');
              mediaQueryList.addListener(mql => {
                  if (mql.matches) {
                      // before print
                      beforePrint();
                  }
              });
          }
          document.body.addEventListener('keydown', e => {
              if (isCommandP(e)) {
                  e.preventDefault();
                  beforePrint();
                  setTimeout(() => window.print(), 200);
              }
          });
      };

      const throttleProgressBar = throttleFrame();
      const throttleRender = throttleTime(100);
      const throttleResize = throttleTime(50);
      const pageSpread = (...pgs) => {
          return div('.spread-wrapper.spread-centered.spread-size', ...pgs);
      };
      class Viewer {
          constructor({ pageSetup, mode, layout, marks }) {
              this.hasRendered = false;
              this.pageSetup = pageSetup;
              this.controls = new Controls();
              this.updateControls();
              this.progressBar = div('.progress-bar');
              this.content = div('.zoom-content');
              this.scaler = div('.zoom-scaler', this.content);
              this.element = div('.root', this.progressBar, this.controls.element, this.scaler);
              this.isDoubleSided = true;
              this.sheetLayout = layout;
              this.marks = marks;
              this.mode = mode;
              this.element.classList.add(classes.viewPreview);
              this.currentLeaf = 0;
              listenForPrint(() => {
                  this.mode = ViewerMode.PRINT;
                  this.render();
              });
              document.body.addEventListener('keydown', (e) => {
                  var _a, _b;
                  switch (e.key) {
                      case 'ArrowLeft':
                          const prev = (_a = this.currentResult) === null || _a === void 0 ? void 0 : _a.previous;
                          if (prev)
                              prev();
                          break;
                      case 'ArrowRight':
                          const next = (_b = this.currentResult) === null || _b === void 0 ? void 0 : _b.next;
                          if (next)
                              next();
                          break;
                  }
              });
              window.addEventListener('resize', () => {
                  throttleResize(() => this.scaleToFit());
              });
              this.setInProgress(true);
              this.setMarks(marks);
              this.show();
          }
          updateControls() {
              this.controls.update({
                  // Initial props
                  paper: this.pageSetup.paper,
                  layout: this.sheetLayout,
                  mode: this.mode,
                  marks: this.marks,
                  pageSize: this.pageSetup.displaySize,
              }, {
                  // Actions
                  setMode: this.setMode.bind(this),
                  setPaper: this.setSheetSize.bind(this),
                  setLayout: this.setLayout.bind(this), // actions.setLayout
                  setMarks: this.setMarks.bind(this),
              });
          }
          setMode(newMode) {
              if (newMode === this.mode)
                  return;
              this.mode = newMode;
              this.updateControls();
              this.render();
          }
          get isInProgress() {
              return this.element.classList.contains(classes.inProgress);
          }
          setInProgress(newVal) {
              this.element.classList.toggle(classes.inProgress, newVal);
          }
          get isTwoUp() {
              return this.sheetLayout !== SheetLayout.PAGES;
          }
          setShowingCropMarks(newVal) {
              this.element.classList.toggle(classes.showCrop, newVal);
          }
          setShowingBleedMarks(newVal) {
              this.element.classList.toggle(classes.showBleedMarks, newVal);
          }
          setShowingBleed(newVal) {
              this.element.classList.toggle(classes.showBleed, newVal);
          }
          get isViewing() {
              return window.document.body.classList.contains(classes.isViewing);
          }
          set isViewing(newVal) {
              window.document.body.classList.toggle(classes.isViewing, newVal);
          }
          setSheetSize(newVal) {
              this.pageSetup.paper = newVal;
              this.pageSetup.updateStyleVars();
              this.mode = ViewerMode.PRINT;
              this.render();
              this.scaleToFit();
              setTimeout(() => {
                  this.scaleToFit();
              }, 300);
          }
          setLayout(newVal) { // actions.setLayout
              if (newVal === this.sheetLayout)
                  return;
              this.sheetLayout = newVal;

              // milahu was here
              // copy-paste from setSheetSize(newVal)
              var sheetSizeChanged = false;
              if (newVal == SheetLayout.PAGES && this.pageSetup.paper != SheetSize.A5_PORTRAIT) {
                this.pageSetup.paper = SheetSize.A5_PORTRAIT;
                sheetSizeChanged = true;
              }
              else if ((newVal == SheetLayout.SPREADS || newVal == SheetLayout.BOOKLET) && this.pageSetup.paper != SheetSize.A4_LANDSCAPE) {
                this.pageSetup.paper = SheetSize.A4_LANDSCAPE;
                sheetSizeChanged = true;
              }

              this.pageSetup.printTwoUp = this.isTwoUp;
              this.pageSetup.updateStyleVars();
              this.mode = ViewerMode.PRINT;
              this.render();

              // milahu was here
              // copy-paste from setSheetSize(newVal)
              if (sheetSizeChanged) {
                this.scaleToFit();
                setTimeout(() => {
                    this.scaleToFit();
                }, 300);
              }
          }
          setMarks(newVal) {
              this.marks = newVal;
              this.updateControls();
              this.setShowingCropMarks(newVal === SheetMarks.CROP || newVal === SheetMarks.BOTH);
              this.setShowingBleedMarks(newVal === SheetMarks.BLEED || newVal === SheetMarks.BOTH);
          }
          displayError(title, text) {
              this.show();
              if (!this.error) {
                  this.error = errorView(title, text);
                  this.element.append(this.error);
                  scrollToBottom();
                  if (this.book) {
                      const flow = this.book.currentPage.flow;
                      if (flow)
                          flow.currentElement.style.outline = '3px solid red';
                  }
              }
          }
          clear() {
              this.book = undefined;
              this.lastSpreadInProgress = undefined; // TODO: Make this clearer, after first render
              this.content.innerHTML = '';
          }
          show() {
              if (this.element.parentNode)
                  return;
              window.document.body.appendChild(this.element);
              this.isViewing = true;
          }
          hide() {
              var _a;
              // TODO this doesn't work if the target is an existing node
              (_a = this.element.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.element);
              this.isViewing = false;
          }
          render(newBook) {
              if (newBook)
                  this.book = newBook;
              if (!this.book)
                  return;
              this.show();
              this.updateControls();
              this.element.classList.remove(...allModeClasses);
              this.element.classList.add(classForMode(this.mode));
              this.setShowingBleed(this.mode === ViewerMode.PRINT);
              const prevScroll = getScrollAsPercent();
              this.setProgressAmount(1);
              window.requestAnimationFrame(() => {
                  const result = this.renderViewMode();
                  this.currentResult = result;
                  this.content.innerHTML = '';
                  this.content.append(result.element);
                  if (!this.hasRendered)
                      this.hasRendered = true;
                  else
                      scrollToPercent(prevScroll);
                  this.scaleToFit();
              });
          }
          renderViewMode() {
              if (!this.book)
                  throw Error('Book missing');
              const pages = this.book.pages.slice();
              switch (this.mode) {
                  case ViewerMode.PREVIEW:
                      return renderGridViewer(pages, this.isDoubleSided);
                  case ViewerMode.FLIPBOOK:
                      return renderFlipbookViewer(pages, this.isDoubleSided);
                      case ViewerMode.PRINT:
                      return renderSheetViewer(pages, this.isDoubleSided, this.sheetLayout);
                  case ViewerMode.LINEAR:
                      return renderLinearViewer(pages);
                  default:
                      throw Error(`Invalid layout mode: ${this.mode} (type ${typeof this.mode})`);
              }
          }
          setProgressAmount(newVal) {
              if (newVal < 1) {
                  throttleProgressBar(() => {
                      this.progressBar.style.transform = `scaleX(${newVal})`;
                  });
              }
              else {
                  this.progressBar.style.transform = '';
              }
          }
          updateProgress(book, estimatedProgress) {
              this.book = book;
              this.setProgressAmount(estimatedProgress);
              if (!window.document.scrollingElement)
                  return;
              const scroller = window.document.scrollingElement;
              // don't bother rerendering if preview is out of view
              const scrollTop = scroller.scrollTop;
              const scrollH = scroller.scrollHeight;
              const h = scroller.offsetHeight;
              if (scrollH > h * 3 && scrollTop < h)
                  return;
              // don't rerender too often
              throttleRender(() => this.renderProgress(book, estimatedProgress));
          }
          renderProgress(book, estimatedProgress) {
              const needsZoomUpdate = !this.content.firstElementChild;
              const sideBySide = this.mode === ViewerMode.PREVIEW ||
                  (this.mode === ViewerMode.PRINT &&
                      this.sheetLayout !== SheetLayout.PAGES);
              const limit = sideBySide ? 2 : 1;
              book.pages.forEach((page, i) => {
                  if (this.content.contains(page.element) &&
                      page.element.parentNode !== this.content)
                      return;
                  if (this.lastSpreadInProgress &&
                      this.lastSpreadInProgress.children.length < limit) {
                      this.lastSpreadInProgress.append(page.element);
                      return;
                  }
                  this.lastSpreadInProgress = pageSpread(page.element);
                  if (i === 0 && sideBySide) {
                      const spacer = new Page();
                      spacer.element.style.visibility = 'hidden';
                      this.lastSpreadInProgress.insertBefore(spacer.element, this.lastSpreadInProgress.firstElementChild);
                  }
                  this.content.append(this.lastSpreadInProgress);
              });
              if (needsZoomUpdate)
                  this.scaleToFit();
          }
          scaleToFit() {
              if (!this.content.firstElementChild)
                  return;
              const prevScroll = getScrollAsPercent();
              const { xScale, yScale } = this.scaleThatFits();
              const scale = this.mode === ViewerMode.FLIPBOOK
                  ? Math.min(1, xScale, yScale)
                  : Math.min(1, xScale);
              this.scaler.style.transform = `scale(${scale})`;
              scrollToPercent(prevScroll);
          }
          scaleThatFits() {
              var _a, _b;
              const contentEl = (_b = (_a = this.currentResult) === null || _a === void 0 ? void 0 : _a.contentSizer) !== null && _b !== void 0 ? _b : this.content;
              const availableSize = {
                  width: window.innerWidth,
                  height: window.innerHeight - 40,
              };
              // Note use of offsetWidth rather than getBoundingClientRect
              // so we can calculate using untransformed/unscaled dimensions
              const contentSize = {
                  width: contentEl.offsetWidth,
                  height: contentEl.offsetHeight,
              };
              const xScale = availableSize.width / contentSize.width;
              const yScale = availableSize.height / contentSize.height;
              return { xScale, yScale };
          }
      }

      /* global BINDERY_VERSION */
      const vals = (obj) => {
          return Object.keys(obj).map(k => obj[k]);
      };
      const nextFrame = () => new Promise(resolve => {
          requestAnimationFrame(t => resolve(t));
      });
      class Bindery {
          constructor(opts) {
              var _a, _b, _c, _d, _e, _f, _g, _h, _j;
              console.log(`bindery Bindery ${BINDERY_VERSION}`);
              validateRuntimeOptions(opts, {
                  name: 'makeBook',
                  autorun: RuntimeTypes.bool,
                  content: RuntimeTypes.any,
                  view: RuntimeTypes.enum(...vals(ViewerMode)),
                  pageNumberOffset: RuntimeTypes.number,
                  pageSetup: RuntimeTypes.shape({
                      name: 'pageSetup',
                      margin: RuntimeTypes.margin,
                      size: RuntimeTypes.size,
                  }),
                  printSetup: RuntimeTypes.shape({
                      name: 'printSetup',
                      bleed: RuntimeTypes.length,
                      layout: RuntimeTypes.enum(...vals(SheetLayout)),
                      marks: RuntimeTypes.enum(...vals(SheetMarks)),
                      paper: RuntimeTypes.enum(...vals(SheetSize)),
                  }),
                  rules: RuntimeTypes.array,
              });
              this.autorun = (_a = opts.autorun) !== null && _a !== void 0 ? _a : true;
              this.autoupdate = (_b = opts.autoupdate) !== null && _b !== void 0 ? _b : false;
              this.pageSetup = new PageSetup(opts.pageSetup, opts.printSetup);
              const startLayout = (_d = (_c = opts.printSetup) === null || _c === void 0 ? void 0 : _c.layout) !== null && _d !== void 0 ? _d : SheetLayout.PAGES;
              const startMarks = (_f = (_e = opts.printSetup) === null || _e === void 0 ? void 0 : _e.marks) !== null && _f !== void 0 ? _f : SheetMarks.CROP;
              this.viewer = new Viewer({
                  pageSetup: this.pageSetup,
                  mode: (_g = opts.view) !== null && _g !== void 0 ? _g : ViewerMode.PREVIEW,
                  marks: startMarks,
                  layout: startLayout,
              });
              if (!opts.content) {
                  this.viewer.displayError('Content not specified', 'You must include a source element, selector, or url');
                  throw Error('Bindery: You must include a source element or selector');
              }
              if (opts.hasOwnProperty('ControlsComponent')) {
                  this.viewer.displayError('Controls are now included', 'Please remove the controls component');
                  throw Error('Bindery: controls are now included');
              }
              this.rules = attributeRules;
              this.rules.push(new Rule({ pageNumberOffset: (_h = opts.pageNumberOffset) !== null && _h !== void 0 ? _h : 0 }));
              (_j = opts.rules) === null || _j === void 0 ? void 0 : _j.forEach(rule => {
                  if (rule instanceof rules.Rule) {
                      this.rules.push(rule);
                  }
                  else {
                      throw Error(`Bindery: The following is not an instance of Bindery.Rule and will be ignored: ${rule}`);
                  }
              });
              if (this.autorun)
                  this.makeBook(opts.content);
          }
          // Convenience constructor
          static makeBook(opts) {
              var _a;
              opts.autorun = (_a = opts.autorun) !== null && _a !== void 0 ? _a : true;
              return new Bindery(opts);
          }
          cancel() {
              this.viewer.hide();
              if (this.content)
                  this.content.style.display = '';
          }
          async makeBook(contentDescription) {
              try {
                  this.content = await getContentAsElement(contentDescription);
              }
              catch (e) {
                  this.viewer.show();
                  this.viewer.displayError('', e.message);
                  // throw e;
                  return undefined;
              }
              this.content.style.display = '';
              const content = this.content.cloneNode(true);
              this.content.style.display = 'none';
              this.viewer.clear(); // In case we're updating an existing layout
              this.viewer.show();
              this.pageSetup.updateStyleVars();
              this.viewer.setInProgress(true);
              const onProgress = (currentBook, progress) => {
                  this.viewer.updateProgress(currentBook, progress);
              };
              try {
                  const book = await makeBook(content, this.rules, onProgress);
                  this.viewer.setProgressAmount(1);
                  await nextFrame();
                  this.viewer.render(book);
                  this.viewer.setInProgress(false);
                  return book;
              }
              catch (e) {
                  this.viewer.setInProgress(false);
                  this.viewer.displayError("Layout couldn't complete", e.message);
                  // throw e;
                  return undefined;
              }
          }
      }

      ___$insertStyle(`

  .bindery-page {
    width: var(--bindery-page-width);
    height: var(--bindery-page-height);
    position: relative;
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
  }

  .bindery-continuation {
    text-indent: unset !important;
  }

  li.continuation {
    list-style: none !important;
  }

  .bindery-flow-box {
    position: relative;
    margin-top: var(--bindery-margin-top);
    flex: 1 1 auto;
    min-height: 0;
  }

  .bindery-footer {
    margin-top: 8pt;
    margin-bottom: var(--bindery-margin-bottom);
    flex: 0 1 auto;
    z-index: 2;
  }

  .bindery-flow-box,
  .bindery-footer {
    margin-left: var(--bindery-margin-inner);
    margin-right: var(--bindery-margin-outer);
  }

  .bindery-left .bindery-flow-box,
  .bindery-left .bindery-footer {
    margin-left: var(--bindery-margin-outer);
    margin-right: var(--bindery-margin-inner);
  }

  .bindery-page-background {
    position: absolute;
    z-index: 0;
    overflow: hidden;
    top: calc(-1 * var(--bindery-bleed));
    bottom: calc(-1 * var(--bindery-bleed));
    left: calc(-1 * var(--bindery-bleed));
    right: calc(-1 * var(--bindery-bleed));
  }
  .bindery-left > .bindery-page-background {
    right: 0;
  }
  .bindery-right > .bindery-page-background {
    left: 0;
  }

  .bindery-sup {
    font-size: 0.667em;
  }

  .bindery-running-header,
  .bindery-footer {
    font-size: 10pt;
  }

  .bindery-running-header {
    position: absolute;
    text-align: center;
    top: 0.25in;
  }
  .bindery-left .bindery-running-header {
    text-align: left;
    left: var(--bindery-margin-outer);
  }
  .bindery-right .bindery-running-header {
    right: var(--bindery-margin-outer);
    text-align: right;
  }

  .bindery-page-size-rotated {
    height: var(--bindery-page-width);
    /* milahu
    width: var(--bindery-page-height);
    */
  }

  .bindery-spread-size {
    /* milahu
    height: var(--bindery-page-height);
    */
    width: calc(var(--bindery-page-width) * 2);
  }

  .bindery-spread-size-rotated {
    /* milahu
    width: var(--bindery-page-height);
    */
    height: calc(var(--bindery-page-width) * 2);
  }

  .bindery-spread.bindery-right > .bindery-page-background {
    left: calc(-100% - var(--bindery-bleed));
  }

  .bindery-spread.bindery-left > .bindery-page-background {
    right: calc(-100% - var(--bindery-bleed));
  }

  @media screen {
    .bindery-viewing .bindery-controls {
      display: flex !important;
    }
  }

  .bindery-controls {
    font: 14px/1.4 -apple-system, BlinkMacSystemFont, "Roboto", sans-serif;
    display: none;
    flex-direction: row;
    align-items: start;
    position: sticky;
    top: 0;
    left: 0;
    right: 0;
    z-index: 99;
    margin: auto;
    color: var(--bindery-ui-text);
    padding: 8px;
    overflow: hidden;
    -webkit-font-smoothing: antialiased;
  }
  .bindery-controls * {
    font: inherit;
    color: inherit;
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  .bindery-controls a {
    color: var(--bindery-ui-accent);
    text-decoration: none;
  }

  .bindery-row {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: start;
    cursor: default;
    user-select: none;
  }

  .bindery-controls .bindery-btn {
    -webkit-appearance: none;
    cursor: pointer;
    display: inline-block;
    margin-right: 8px;
    text-decoration: none;
  }
  .bindery-controls .bindery-btn:hover {
    background: rgba(0, 0, 0, 0.04);
  }
  .bindery-controls .bindery-btn:active {
    background: rgba(0, 0, 0, 0.08);
  }
  .bindery-controls .bindery-btn:last-child {
    margin-right: 0;
  }

  .bindery-control {
    border-radius: 6px;
    color: var(--bindery-ui-text);
    padding: 4px 8px;
    border: 1px solid #ddd;
    margin-right: 12px;
  }

  .bindery-controls .bindery-btn-main {
    position: absolute;
    top: 8px;
    right: 8px;
    /*
    background: var(--bindery-ui-accent);
    border-color: var(--bindery-ui-accent);
    color: white;
    */
  }
  /*
  .bindery-controls .bindery-btn-main:hover {
    background: var(--bindery-ui-accent);
    opacity: 0.7;
  }
  .bindery-controls .bindery-btn-main:active {
    background: black;
    opacity: 1;
  }
  .bindery-controls .bindery-btn-main:focus {
    outline: 0;
    box-shadow: 0 0 0 1px var(--bindery-ui-bg), 0 0 0 3px rgba(0, 0, 0, 0.3);
  }
  */

 .bindery-controls .bindery-btn-main:hover {
    background: rgba(0, 0, 0, 0.04);
  }
  .bindery-controls .bindery-btn-main:active {
    background: rgba(0, 0, 0, 0.08);
  }


  .bindery-view-row {
    transition: all 0.3s;
  }
  .bindery-in-progress .bindery-view-row {
    opacity: 0;
    pointer-events: none;
  }
  .bindery-debug .bindery-view-row {
    display: none;
  }

  .bindery-btn-print {
    margin-left: auto;
    transition: all 0.3s;
  }
  .bindery-in-progress .bindery-btn-print {
    opacity: 0;
    pointer-events: none;
  }

  .bindery-controls .bindery-select-wrap {
    padding-right: 24px;
    transition: all 0.2s;
    white-space: nowrap;
    width: 100%;
    position: relative;
  }
  .bindery-controls .bindery-select-wrap:after {
    content: "";
    position: absolute;
    right: 9px;
    top: 12px;
    border-bottom: 1px solid;
    border-right: 1px solid;
    padding: 0px;
    border: 4px solid transparent;
    border-top-color: currentColor;
  }
  .bindery-controls .bindery-select-wrap:hover {
    background: rgba(0, 0, 0, 0.04);
  }
  .bindery-controls .bindery-select-wrap:active {
    background: rgba(0, 0, 0, 0.08);
  }
  .bindery-controls .bindery-select-wrap:focus-within {
    outline: 0;
    box-shadow: 0 0 0 1px var(--bindery-ui-bg), 0 0 0 3px rgba(0, 0, 0, 0.3);
  }

  .bindery-select {
    cursor: pointer;
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 4px 8px;
    color: black;
    border: transparent;
    width: 100%;
    font-size: 18px;
  }

  .bindery-view-print .bindery-controls {
    background: var(--bindery-ui-bg);
  }

  /*
  @media screen and (max-width: 960px) {
  */
 /* switch: horizontal menu - vertical menu. maybe useful for narrow screens (mobile)
  @media screen and (max-width: 500px) {
    .bindery-view-print .bindery-controls,
    .bindery-view-preview .bindery-controls,
    .bindery-view-linear .bindery-controls {
      background: var(--bindery-ui-bg);
      flex-direction: column;
    }
  }
  */
  @media screen and (max-width: 500px) {
    .bindery-view-print .bindery-controls {
      background: var(--bindery-ui-bg);
    }

    .bindery-view-row {
      margin-bottom: 8px;
    }

    .bindery-print-options {
      flex-direction: column;
      align-items: stretch;
      width: 100%;
    }
    .bindery-print-options .bindery-row + .bindery-row {
      margin-top: 8px;
    }
    .bindery-print-options .bindery-select-wrap {
      margin: 0;
    }
  }
  .bindery-rotate-container.bindery-rotate-clockwise,
  .bindery-left .bindery-rotate-container.bindery-rotate-spread-clockwise,
  .bindery-right .bindery-rotate-container.bindery-rotate-inward,
  .bindery-left .bindery-rotate-container.bindery-rotate-outward {
    transform: rotate(90deg) translate3d(0, -100%, 0);
    transform-origin: top left;
  }

  .bindery-rotate-container.bindery-rotate-counterclockwise,
  .bindery-left .bindery-rotate-container.bindery-rotate-spread-counterclockwise,
  .bindery-left .bindery-rotate-container.bindery-rotate-inward,
  .bindery-right .bindery-rotate-container.bindery-rotate-outward {
    transform: rotate(-90deg) translate3d(-100%, 0, 0);
    transform-origin: top left;
  }

  .bindery-rotate-container {
    position: absolute;
  }
  .bindery-left .bindery-rotate-container.bindery-rotate-clockwise .bindery-page-background {
    top: 0;
  }
  .bindery-right .bindery-rotate-container.bindery-rotate-clockwise .bindery-page-background {
    bottom: 0;
  }
  .bindery-left .bindery-rotate-container.bindery-rotate-counterclockwise .bindery-page-background {
    bottom: 0;
  }
  .bindery-right .bindery-rotate-container.bindery-rotate-counterclockwise .bindery-page-background {
    top: 0;
  }
  .bindery-rotate-container.bindery-rotate-inward .bindery-page-background {
    bottom: 0;
  }
  .bindery-rotate-container.bindery-rotate-outward .bindery-page-background {
    top: 0;
  }
  .bindery-right .bindery-rotate-container.bindery-rotate-spread-clockwise {
    transform: rotate(90deg) translate3d(0, -50%, 0);
    transform-origin: top left;
  }
  .bindery-right .bindery-rotate-container.bindery-rotate-spread-counterclockwise {
    transform: rotate(-90deg) translate3d(-100%, -50%, 0);
    transform-origin: top left;
  }

  :root {
    --bindery-ui-bg: #f4f4f4;
    --bindery-ui-accent: black;
    --bindery-ui-text: black;
  }

  @media screen {
    .bindery-root {
      transition: opacity 0.2s;
      opacity: 1;
      background: var(--bindery-ui-bg);
      z-index: 99;
      position: relative;
      min-height: 100vh;
    }

    .bindery-progress-bar {
      transform-origin: top left;
      transform: scaleY(0);
      position: fixed;
      left: 0;
      top: 0;
      right: 0;
      background: var(--bindery-ui-accent);
      transition: transform 0.2s;
      height: 2px;
      z-index: 99;
    }
    .bindery-in-progress .bindery-progress-bar {
      transform: scaleX(0);
    }

    .bindery-zoom-content {
      padding: 10px;
      background: var(--bindery-ui-bg);
      margin: auto;
    }
    .bindery-view-preview .bindery-zoom-content {
      min-width: calc(20px + var(--bindery-spread-width));
    }
    .bindery-view-linear .bindery-zoom-content {
      min-width: calc(20px + var(--bindery-page-width));
    }

    .bindery-view-linear .bindery-page {
      zoom: 2; /* quickfix */ /* FIXME this breaks on mobile screens. pages overflow, overflow is hidden */
    }
    @media screen and (max-width: 960px) {
      .bindery-view-linear .bindery-page {
        zoom: 1; /* quickfix for mobile screens */
      }
    }

    .bindery-view-print .bindery-zoom-content {
      min-width: calc(20px + var(--bindery-sheet-width));
    }
    .bindery-zoom-content > .bindery-page {
      margin: auto;
    }

    .bindery-measure-area {
      position: fixed;
      padding: 50px 20px;
      z-index: 99;
      visibility: hidden;
      top: 0;
      left: 0;
      width: 0;
      height: 0;
      overflow: hidden;
    }

    .bindery-is-overflowing {
      border-bottom: 1px solid magenta;
    }

    .bindery-print-sheet {
      margin: 0 auto;
    }

    .bindery-error {
      font: 16px/1.4 -apple-system, BlinkMacSystemFont, "Roboto", sans-serif;
      padding: 15vh 15vw;
      z-index: 999;
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: rgba(244, 244, 244, 0.7);
    }
    .bindery-error-title {
      font-size: 1.5em;
      margin-bottom: 16px;
    }
    .bindery-error-text {
      margin-bottom: 16px;
      white-space: pre-line;
    }
    .bindery-error-footer {
      opacity: 0.5;
      font-size: 0.66em;
      text-transform: uppercase;
      letter-spacing: 0.02em;
    }

    .bindery-show-bleed .bindery-print-sheet {
      background: white;
      outline: 1px solid rgba(0, 0, 0, 0.1);
      box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2);
      margin: 20px auto;
    }
  }
  @media screen {
    .bindery-page {
      background: white;
      box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.3);
      overflow: hidden;
    }
    .bindery-show-bleed .bindery-page {
      box-shadow: none;
      overflow: visible;
    }
    .bindery-show-bleed .bindery-page:after {
      content: "";
      outline: 1px dotted rgba(0, 255, 255, 0.7);
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      top: 0;
      z-index: 99;
    }

    .bindery-placeholder-num {
      visibility: hidden !important;
    }
  }
  .bindery-print-sheet {
    width: var(--bindery-sheet-width);
    height: var(--bindery-sheet-height);
  }

  .bindery-page-bleed-clip {
    overflow: hidden;
  }

  .bindery-page-bleed-clip-left {
    padding-left: calc(var(--bindery-bleed) + 12pt);
  }

  .bindery-page-bleed-clip-right {
    padding-right: calc(var(--bindery-bleed) + 12pt);
  }

  .bindery-show-crop .bindery-print-sheet .bindery-page-bleed-clip,
  .bindery-show-bleed-marks .bindery-print-sheet .bindery-page-bleed-clip {
    padding-top: calc(var(--bindery-bleed) + 12pt);
    padding-bottom: calc(var(--bindery-bleed) + 12pt);
  }

  .bindery-print-sheet-spread .bindery-spread-wrapper {
    margin-left: auto;
    margin-right: auto;
  }

  .bindery-viewing {
    margin: 0;
  }

  .bindery-zoom-scaler {
    transform-origin: top left;
    transform-style: preserve-3d;
  }

  .bindery-print-sheet {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s;
  }

  .bindery-print-sheet-left {
    justify-content: left;
  }

  .bindery-print-sheet-right {
    justify-content: right;
  }

  .bindery-spread-wrapper {
    position: relative;
    display: flex;
    justify-content: center;
    margin: auto;
  }

  .bindery-spread-centered {
    margin: 0 auto 32px;
  }

  @page {
    margin: 0;
  }
  @media print {
    .bindery-root * {
      -webkit-print-color-adjust: exact;
      color-adjust: exact;
    }

    .bindery-controls {
      display: none !important;
    }

    .bindery-print-sheet {
      contain: layout;
      /* prevent margin collapse */
      margin: 0 auto;
      page-break-after: always;
    }

    .bindery-zoom-scaler[style] {
      transform: none !important;
    }
  }
  .bindery-print-mark-wrap {
    display: none;
    position: absolute;
    pointer-events: none;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 999;
    margin: auto;
  }
  .bindery-show-crop .bindery-print-mark-wrap, .bindery-show-bleed-marks .bindery-print-mark-wrap {
    display: block;
  }
  .bindery-show-crop .bindery-print-mark-wrap > [class*=crop] {
    display: block;
  }
  .bindery-show-bleed-marks .bindery-print-mark-wrap > [class*=bleed] {
    display: block;
  }
  .bindery-print-mark-wrap > div {
    display: none;
    position: absolute;
    overflow: hidden;
  }
  .bindery-print-mark-wrap > div::before, .bindery-print-mark-wrap > div::after {
    content: "";
    display: block;
    position: absolute;
  }
  .bindery-print-mark-wrap > div:before {
    top: 0;
    left: 0;
  }
  .bindery-print-mark-wrap > div:after {
    bottom: 0;
    right: 0;
  }

  .bindery-mark-crop-fold,
  .bindery-mark-crop-left,
  .bindery-mark-crop-right,
  .bindery-mark-bleed-left,
  .bindery-mark-bleed-right {
    width: 1px;
    margin: auto;
  }
  .bindery-mark-crop-fold::before, .bindery-mark-crop-fold:after, .bindery-mark-crop-left::before, .bindery-mark-crop-left:after, .bindery-mark-crop-right::before, .bindery-mark-crop-right:after, .bindery-mark-bleed-left::before, .bindery-mark-bleed-left:after, .bindery-mark-bleed-right::before, .bindery-mark-bleed-right:after {
    width: 1px;
    height: var(--bindery-mark-length);
    background-image: linear-gradient(to right, black 0%, black 51%, transparent 51%);
    background-size: 1px 100%;
  }

  .bindery-mark-crop-top,
  .bindery-mark-crop-bottom,
  .bindery-mark-bleed-top,
  .bindery-mark-bleed-bottom {
    height: 1px;
  }
  .bindery-mark-crop-top::before, .bindery-mark-crop-top:after, .bindery-mark-crop-bottom::before, .bindery-mark-crop-bottom:after, .bindery-mark-bleed-top::before, .bindery-mark-bleed-top:after, .bindery-mark-bleed-bottom::before, .bindery-mark-bleed-bottom:after {
    width: var(--bindery-mark-length);
    height: 1px;
    background-image: linear-gradient(to bottom, black 0%, black 51%, transparent 51%);
    background-size: 100% 1px;
  }

  .bindery-mark-crop-fold {
    right: 0;
    left: 0;
  }
  .bindery-mark-crop-left {
    left: 0;
  }
  .bindery-mark-crop-right {
    right: 0;
  }
  .bindery-mark-crop-top {
    top: 0;
  }
  .bindery-mark-crop-bottom {
    bottom: 0;
  }

  .bindery-print-meta {
    padding: var(--bindery-mark-length);
    text-align: center;
    font-family: -apple-system, BlinkMacSystemFont, "Roboto", sans-serif;
    font-size: 8pt;
    display: block !important;
    position: absolute;
    bottom: -60pt;
    left: 0;
    right: 0;
  }

  .bindery-mark-bleed-left,
  .bindery-mark-bleed-right,
  .bindery-mark-crop-left,
  .bindery-mark-crop-right,
  .bindery-mark-crop-fold {
    top: calc(-1 * var(--bindery-mark-length) - var(--bindery-bleed));
    bottom: calc(-1 * var(--bindery-mark-length) - var(--bindery-bleed));
  }

  .bindery-mark-bleed-top,
  .bindery-mark-bleed-bottom,
  .bindery-mark-crop-top,
  .bindery-mark-crop-bottom {
    left: calc(-12pt - var(--bindery-bleed));
    right: calc(-12pt - var(--bindery-bleed));
  }

  .bindery-mark-bleed-left {
    left: calc(-1 * var(--bindery-bleed));
  }

  .bindery-mark-bleed-right {
    right: calc(-1 * var(--bindery-bleed));
  }

  .bindery-mark-bleed-top {
    top: calc(-1 * var(--bindery-bleed));
  }

  .bindery-mark-bleed-bottom {
    bottom: calc(-1 * var(--bindery-bleed));
  }

  .bindery-root.bindery-view-flip {
    max-height: 100vh;
  }

  .bindery-view-flip .bindery-zoom-holder {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin: auto;
    display: block;
  }
  .bindery-view-flip .bindery-zoom-scaler {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin: auto;
    transform-origin: center;
    width: 0;
    height: 0;
  }
  .bindery-view-flip .bindery-zoom-content {
    min-width: 0;
  }

  .bindery-flap-holder {
    perspective: 5000px;
    transform-style: preserve-3d;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin: auto;
  }

  .bindery-flipbook-sizer {
    --bindery-flipbook-width: calc(var(--bindery-spread-width) * 1.15);
    --bindery-flipbook-height: calc(var(--bindery-page-height) * 1.05);
    position: absolute;
    width: var(--bindery-flipbook-width);
    height: var(--bindery-flipbook-height);
    top: calc(var(--bindery-flipbook-height) * -0.5);
    left: calc(var(--bindery-flipbook-width) * -0.5);
  }

  .bindery-page3d {
    margin: auto;
    width: var(--bindery-page-width);
    /* milahu
    height: var(--bindery-page-height);
    */
    transform: rotateY(0);
    transform-style: preserve-3d;
    transform-origin: left;
    transition: transform 0.5s, box-shadow 0.1s;
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
  }
  .bindery-page3d:hover {
    box-shadow: 2px 0 4px rgba(0, 0, 0, 0.2);
  }
  .bindery-page3d.bindery-flipped {
    transform: rotateY(-180deg);
  }
  .bindery-page3d .bindery-page {
    position: absolute;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
  }
  .bindery-page3d .bindery-page3d-front {
    transform: rotateY(0);
  }
  .bindery-page3d .bindery-page3d-back {
    transform: rotateY(-180deg);
  }

  `);

      /* global BINDERY_VERSION */
      const constants = {
          View: ViewerMode,
          Paper: SheetSize,
          Layout: SheetLayout,
          Marks: SheetMarks,
          version: BINDERY_VERSION
      };
      const BinderyWithRules = Object.assign(Bindery, rules, constants);

      return BinderyWithRules;

  })));

</script>



<script>Bindery.makeBook(binderyOptions(Bindery))</script>

</body>
</html>
